Poperton
Poperton

Reputation: 2127

@@ and |> operators precedence in OCaml

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

Answers (2)

Jeffrey Scofield
Jeffrey Scofield

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

Anthony Scemama
Anthony Scemama

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

Related Questions