When to use piping |> versus arguments
Asked Answered
X

2

5

In Reason (and OCaml), there is a non-traditional way of passing arguments using the |> operator. What is the convention for when it should be used? I am currently using it all over the place just because of how novel I find it.

Xerxes answered 15/4, 2018 at 16:2 Comment(3)
If it's anything like Clojure's threading macro -> (passes the result of one expression as the first argument of the next expression, and continues that along all the expressions), it should be used when you have a long chain/pipe of calls that need to be made to transform data. I use such macros everywhere because they get rid of nesting calls and increase readability.Noumenon
@Noumenon In other words, I should always be using them except when it definitely hurts readability ?Xerxes
From a Clojure perspective, yes. Any time I have to "thread" an argument through >= 2 transformations, I reach for a threading macro, unless I have reason to believe that it might hurt readability, or it isn't feasible due to mismatching argument orders. They allow you to more easily add parts to the "pipe" later on, and as the answer points out, leads to a nicer reading order. In Clojure, it's a macro, so there isn't even a runtime cost. I not sure how it works in OCaml though.Noumenon
T
11

Using |> (forward pipe) is helpful for showing the order of executions.

For example, if you want to execute function f, then g like this:

g(f(x))

It's easier to see the order of executions (e.g., f and then g) this way:

x |> f |> g

Programming languages like OCaml or F# are used a lot to transform data from one form to another, so |> can be used that way to show how data got transformed.

let sqr = x => x * x;

[1,2,3]
|> List.map (x => x + 1)
|> List.map (sqr);
Transience answered 15/4, 2018 at 16:27 Comment(2)
When should it NOT be used then?Xerxes
It depends on the style and code readability. I don't see how x |> f is better than f(x), or in OCaml/F#, f x, so I will never use it that way.Transience
S
3

The reverse application operator (|>) can simply be defined as

let (|>) x f = f x

This infix operator takes a value x and a function f and apply the latter to the first (f x). This may not seem apparently useful at first, but the operator is powerful when used correctly because functions in Ocaml are curried.

For example, let's say we had a function wackymath: int -> int -> int -> int

let wackymath a b c = a + b - c

The type of wackymath is int -> int -> int -> int. This is because in a functional realm (specifically, lambda calculus), any function only applies to one argument at a time. Therefore, with the help of parentheses, the order of application of wackymath looks like this:

(((wackymath a) b) c)

Argument substitution could make this clearer.

let f1 = wackymath 10;; (* 10 + b - c *)
let f2 = f1 19;;        (* 10 + 19 - c *)  
f2 4;;                  (* 10 + 19 - 4 = 25 *)

This could be expressed with the |> operator as such:

4 |> (19 |> (10 |> wackymath));;

Now it's clear why it's called reverse application operator. The parentheses are there because |> is left-associative. Saying |> helps avoid parentheses are not exactly precise in all cases.

Usually the operator is useful in situations when you want to compose a series of sequential function applications

[1; 2; 3; 4; 5]
|> List.map (fun x -> x * 2)
|> List.filter (fun x -> x < 3)
|> fun l -> match l with
   | [] -> 0
   | l' -> l' |> List.fold_left ~init:0 ~f:(fun a b -> a + b)
;;
Stimson answered 16/4, 2018 at 22:47 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.