loading...

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

splodingsocks profile image Murphy Randle ・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;

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");

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");

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

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

markdown guide
 

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

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

proof

Any thoughts?

 

leaving this gist just in case this become unavailable

 

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?

 
 

Quick question, is |> same as ->?

 

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).