This is the latest docs version
Quick Links
  • -Overview
  • -Language Features
  • -JS Interop
  • -Build System
Documentation
Language Manual
Reference for all language features
ReScript & React
First class bindings for ReactJS
GenType
Seamless TypeScript integration
Reanalyze
Dead Code & Termination analysis
Exploration
Packages
Explore third party libraries and bindings
Syntax Lookup
Discover all syntax constructs
APIPlaygroundBlogCommunity
  • Playground
  • Blog
  • Twitter
  • GitHub
  • Forum
Language Manual
Overview
  • Introduction
  • Installation
  • Migrate to v11
  • Editor Plugins
  • Try
Language Features
  • Overview
  • Let Binding
  • Type
  • Primitive Types
  • Tuple
  • Record
  • Object
  • Variant
  • Polymorphic Variant
  • Null, Undefined and Option
  • Array & List
  • Function
  • If-Else & Loops
  • Pipe
    • Tips & Tricks
    • JS Method Chaining
    • Pipe Into Variants
    • Pipe Placeholders
    • Triangle Pipe (Deprecated)
  • Pattern Matching / Destructuring
  • Mutation
  • JSX
  • Exception
  • Lazy Value
  • Promises
  • Async / Await
  • Tagged templates
  • Module
  • Import & Export
  • Attribute (Decorator)
  • Reserved Keywords
  • Equality and Comparison
Advanced Features
  • Extensible Variant
  • Scoped Polymorphic Types
JavaScript Interop
  • Interop Cheatsheet
  • Embed Raw JavaScript
  • Shared Data Types
  • External (Bind to Any JS Library)
  • Bind to JS Object
  • Bind to JS Function
  • Import from / Export to JS
  • Bind to Global JS Values
  • JSON
  • Inlining Constants
  • Use Illegal Identifier Names
  • Generate Converters & Helpers
  • Browser Support & Polyfills
  • Libraries & Publishing
  • TypeScript
Build System
  • Overview
  • Configuration
  • Configuration Schema
  • External Stdlib
  • Pinned Dependencies
  • Interop with JS Build Systems
  • Performance
  • Warning Numbers
Guides
  • Converting from JS
Extra
  • Newcomer Examples
  • Project Structure
  • FAQ
Docs / Language Manual / Pipe
Edit

Pipe

ReScript provides a tiny but surprisingly useful operator ->, called the "pipe", that allows you to "flip" your code inside-out. a(b) becomes b->a. It's a simple piece of syntax that doesn't have any runtime cost.

Why would you use it? Imagine you have the following:

ReScriptJS Output
validateAge(getAge(parseData(person)))

This is slightly hard to read, since you need to read the code from the innermost part, to the outer parts. Use pipe to streamline it:

ReScriptJS Output
person
  ->parseData
  ->getAge
  ->validateAge

Basically, parseData(person) is transformed into person->parseData, and getAge(person->parseData) is transformed into person->parseData->getAge, etc.

This works when the function takes more than one argument too.

ReScriptJS Output
a(one, two, three)

is the same as

ReScriptJS Output
one->a(two, three)

This also works with labeled arguments.

Pipes are used to emulate object-oriented programming. For example, myStudent.getName in other languages like Java would be myStudent->getName in ReScript (equivalent to getName(myStudent)). This allows us to have the readability of OOP without the downside of dragging in a huge class system just to call a function on a piece of data.

Tips & Tricks

Do not abuse pipes; they're a means to an end. Inexperienced engineers sometimes shape a library's API to take advantage of the pipe. This is backwards.

JS Method Chaining

This section requires understanding of our binding API.

JavaScript's APIs are often attached to objects, and are often chainable, like so:

JS
const result = [1, 2, 3].map(a => a + 1).filter(a => a % 2 === 0); asyncRequest() .setWaitDuration(4000) .send();

Assuming we don't need the chaining behavior above, we'd bind to each case of this using @send from the aforementioned binding API page:

ReScriptJS Output
type request
@val external asyncRequest: unit => request = "asyncRequest"
@send external setWaitDuration: (request, int) => request = "setWaitDuration"
@send external send: request => unit = "send"

You'd use them like this:

ReScriptJS Output
let result = Array.filter(
  Array.map([1, 2, 3], a => a + 1),
  a => mod(a, 2) == 0
)

send(setWaitDuration(asyncRequest(), 4000))

This looks much worse than the JS counterpart! Clean it up visually with pipe:

ReScriptJS Output
let result = [1, 2, 3]
  ->Array.map(a => a + 1)
  ->Array.filter(a => mod(a, 2) == 0)

asyncRequest()->setWaitDuration(4000)->send

Pipe Into Variants

You can pipe into a variant's constructor as if it was a function:

ReScriptJS Output
let result = name->preprocess->Some

We turn this into:

ReScriptJS Output
let result = Some(preprocess(name))

Note that using a variant constructor as a function wouldn't work anywhere else beside here.

Pipe Placeholders

A placeholder is written as an underscore and it tells ReScript that you want to fill in an argument of a function later. These two have equivalent meaning:

RES
let addTo7 = (x) => add3(3, x, 4) let addTo7 = add3(3, _, 4)

Sometimes you don't want to pipe the value you have into the first position. In these cases you can mark a placeholder value to show which argument you would like to pipe into.

Let's say you have a function namePerson, which takes a person then a name argument. If you are transforming a person then pipe will work as-is:

ReScriptJS Output
makePerson(~age=47)
  ->namePerson("Jane")

If you have a name that you want to apply to a person object, you can use a placeholder:

ReScriptJS Output
getName(input)
  ->namePerson(personDetails, _)

This allows you to pipe into any positional argument. It also works for named arguments:

ReScriptJS Output
getName(input)
  ->namePerson(~person=personDetails, ~name=_)

Triangle Pipe (Deprecated)

You might see usages of another pipe, |>, in some codebases. These are deprecated.

Unlike -> pipe, the |> pipe puts the subject as the last (not first) argument of the function. a |> f(b) turns into f(b, a).

For a more thorough discussion on the rationale and differences between the two operators, please refer to the Data-first and Data-last comparison by Javier Chávarri

If-Else & LoopsPattern Matching / Destructuring

© 2024 The ReScript Project

Software and assets distribution powered by KeyCDN.

About
  • Community
  • ReScript Association
Find us on