Reputation: 2127
I'm trying to understand 2 OCaml operators: @@
and |>
I understand that x |> f
is just f(x)
, but why it exists? I cannot see why. The same for @@
, which as I unferstood, is just normal function application
For example:
match get_ipv4_hlen_version buf |> version with
| 0x40 -> Ok buf
| n -> Error (Printf.sprintf "IPv4 presented with a packet that claims a different IP version: %x" n)
why not write just get_ipv4_hlen_version version buf
?
What about
let options_len = nearest_4 @@ Cstruct.len t.options
why not let options_len = nearest_4 Cstruct.len t.options
?
I suppose it has to do with precedence, I recall some of these things from Haskell but I don't know Haskell I just read somewhere.
How do I know the precedence of things?
if more context is needed, these 2 codes came from https://github.com/mirage/mirage-tcpip/blob/master/src/ipv4/ipv4_packet.ml
Upvotes: 1
Views: 1141
Reputation: 66818
The notational value of |>
only appears if you have several nested function applications. Many people find this:
x |> f a |> g b c |> h d
easier to read than this:
h d (g b c (f a x))
because it's no longer necessary to match up the parentheses mentally, and because the operations are applied in left-to-right order (which is arguably natural for readers of English and other left-to-right languages).
If you are familiar with Unix command lines, it might help to think of the |>
operator as similar to the Unix pipe operator |
.
A lower-precedence function application operator like @@
also helps avoid parentheses (and mental matching thereof). Many people find this:
f x @@ g a b @@ h c d
easier to read than this:
f x ((g a b) (h c d))
Your example for @@ is wrong. This
let options_len = nearest_4 @@ Cstruct.len t.options
is equivalent to this:
let options_len = nearest_4 (Cstruct.len t.options)
and is not equivalent to what you wrote.
The precedence of an operator is determined by its first character. This, in turn, is defined by the table in Section 7.7.1 of the OCaml manual.
(Granted, you need to read very carefully the text just before the table to see the rule for precedence.)
Update
Full disclosure: I never use |>
or @@
in my own code. I have no problem with a few parentheses, and I generally use let
to break a big expression down into smaller pieces.
Upvotes: 7
Reputation: 1593
The |>
operator is very convenient. It is the equivalent of the pipe in the shell. It allows you to write code like this:
let make_string n =
Array.init n float_of_int
|> Array.map (fun x -> x -. 0.5 *. (float_of_int (n-1)))
|> Array.map (fun x -> Printf.sprintf "-- %10.6f --" x)
|> Array.to_list
|> String.concat "\n"
in
make_string 5
(* Output:
-- -2.000000 --
-- -1.000000 --
-- 0.000000 --
-- 1.000000 --
-- 2.000000 --
*)
In this example, each line starting with a |>
takes the output of the previous transformation, so we can see the flow of data transformations, like in Bash when we write something like
ls | grep txt | sort | uniq
The @@
operator is the "backwards pipe". It allows to remove parenthesis that would make the code less readable. For example, take the case where we want to make a chain of matrix products like C = A.B.C.D. You want the code to be consistent with the mathematical formula, so you want to write it in the same order. If mm A B
makes the matrix multiplication of A and B, then we can write
let mat_C =
mm mat_A @@ mm mat_B @@ mm mat_C mat_D
instead of
let mat_C =
mm mat_A (mm mat_B (mm mat_C mat_D))
Upvotes: 4