DEV Community

loading...

A quick explanation about fast piping (-> and |.) in ReasonML

Murphy Randle
I’m a professional software writer specialized in Web technologies and engaged in indie application development. I consider myself to be a man of faith. I pursue honesty, integrity, and compassion.
・2 min read

Introduction

There's a neat operator in ReasonML that's called a "fast pipe" it looks like this: -> or like this |.. And it allows for a nice visual flow when calling functions that take a type as their first argument (it we were using an object-oriented language, these functions might be defined as methods on an object).

Why two syntaxes?

The new release of ReasonML 3.3.x deprecated the use of |. as the fast pipe operator, preferring instead the skinny arrow syntax: ->. We'll probably see both of them in documentation and examples for a while.

What does it actually do?

The fast pipe takes everything on the left side of the operator, and applies the result of that expression as the first argument of the function on the right side of the operator.

This sort of simulates what it feels like to call a method on an object in an object-oriented language.

For example, if we had a class Person in Python, with a method greet, that took a message as a string, we could call it like this:

personA.greet("hello").

In Reason, the practice is to create a person type, but keep any functions that work on the person type as stand-alone functions that take the person type as the first argument. Something like this:

type person;
let greet = (person, string) => string;
Enter fullscreen mode Exit fullscreen mode

See above that greet takes a person, and a string, and returns another string that, presumably, greets that person.

To call this in Reason without fast piping, we'd do this:

let greeting = greet(personA, "hello");
Enter fullscreen mode Exit fullscreen mode

But if we want to feel more like greet is a method instead of a standalone function, we can use the fast-pipe operator:

let greeting = personA->greet("hello");
/* or */
let greeting = personA|.greet("hello");
Enter fullscreen mode Exit fullscreen mode

See how the thing on the left (personA) gets applied as the first argument of the function on the right?

This might seem marginally useful. But we really start to see the beauty once we start chaining function calls:

maybeSomething->Belt.Option.map(a => a)->Belt.Option.getExn
Enter fullscreen mode Exit fullscreen mode

We end up with a calling structure that looks a bit like the 'ol JQuery method chaining pattern! This ends up being extra super convenient when working a lot with code that wraps Javascript libraries, since so many JS libraries use object methods heavily.

In Summary

"Fast piping" is a way to conveniently call functions that take an object (or type) as their first parameter. The preferred syntax is ->, but is also exists as |.. Fast piping is very useful for the APIs found in the 'Belt' standard module, and can be super duper useful when wrapping object-oriented Javascript APIs.

Discussion (9)

Collapse
ryuheechul profile image
Heechul Ryu

I found something interesting and it's probably a bug.

-> doesn't work after |> when |. still works after |>

proof

Any thoughts?

Collapse
ryuheechul profile image
Heechul Ryu

leaving this gist just in case this become unavailable

Collapse
mrmurphy profile image
Murphy Randle Author

Nice find! That’s a good question. I sometimes have to end up grouping the left side of -> with parenthesis while throwing |> in the mix. I wonder if that’s an implementation bug?

Collapse
ryuheechul profile image
Heechul Ryu

I think so, sir! I just left an issue for this.

Thread Thread
mrmurphy profile image
Murphy Randle Author

Thanks for doing that!

Collapse
ryuheechul profile image
Heechul Ryu

Quick question, is |> same as ->?

Collapse
hoichi profile image
Sergey Samokhov

Nope. |> is a regular pipe and it passes the result of the left expression as a last argument of the function on the right. This is why, unlike Belt, in a lot (maybe in the most) of FP libraries functions like map have a signature of map(f, list), not map(list, f), because you’re supposed to call them like: someList |> map(f1) |> filter(f2), which is basically the same as filter(f2, map(f1, someList)). But functions in Belt have the same parameter order that in the vanilla JS (maybe that was one of the reasons), like this: map(list, f). That means that the regular pipe won’t work, so you have to use fast pipes, that looks about the same, someList -> map(f1) -> filter(f2), but are desugared as: filter(map(someList, f1), f2).

The other reason why fast pipe (and the corresponding parameter order) might be preferable is that it’s better for completion. Once you type expression ->, your IDE can suggest some functions the first argument of which has the type of expression. With regular pipes, it’s a more complicated task (for various reasons, most of which I may not understand).

Collapse
mrmurphy profile image
Murphy Randle Author

Great answer!

Collapse
ryuheechul profile image
Heechul Ryu

Great answer! Thanks a lot!