1

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

Guerlando OCs
  • 1,886
  • 9
  • 61
  • 150
  • 1
    Does this answer your question? [OCaml |> operator](https://stackoverflow.com/questions/30493644/ocaml-operator) – Yawar Apr 21 '20 at 22:45

2 Answers2

6

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.

ghilesZ
  • 1,502
  • 1
  • 18
  • 30
Jeffrey Scofield
  • 65,646
  • 2
  • 72
  • 108
  • Is 'f x @@ g a b @@ h c d' the same as 'f x ((g a b) (h c d))' as you say? Isn't it the same as 'f x (g a b (h c d))'? So maybe it is not so much easier to read. – Chris Vine Apr 21 '20 at 23:32
  • Well, you have a point! I tested my example so I think it's right. I believe the point is still valid (maybe for simpler expressions). – Jeffrey Scofield Apr 21 '20 at 23:39
  • also, `f x ((g a b) (h c d))` and `f x (g a b (h c d))` are the same expression, those parens are unnecessary :-) – PatJ Apr 22 '20 at 09:39
  • @PatJ doesn't the first apply partially ````g````? unlike the second one? This could make a difference when let's say ````a```` and ````c```` have side effects. – ghilesZ Apr 22 '20 at 10:01
  • @PatJ. Good point, but would not `f x (g a b (h c d))` require `c` and `d` to be evaluated before `a` and `b`, whereas with `f x ((g a b) (h c d))` their respective order of evaluation would be unspecified? Not sure. – Chris Vine Apr 22 '20 at 10:52
  • @ChrisVine i think ````c```` and ````d```` would not necessarily be evaluated before ````a```` and ````b````. Either both ````c```` and ````d```` are evaluated beferore ````a```` and ````b```` or both after ````a```` and ````b````. A bit confusing but worded differently, either ````(g a b)```` is evaluated first or either ````(h c d)```` is evaluated first. Unlike in ````(g a b (h c d)```` where we could have ````a```` then ````(h c d)```` then ````b````, eventhough i doubt this evaluation order will ever exist in an actual implementation – ghilesZ Apr 22 '20 at 11:04
  • 1
    @ghilesZ Since the order of evaluation of function arguments is unspecified, and because of ocaml's automatic partial application, I am coming round to the view that, in terms of the language, the version with the additional parentheses and the one without are equivalent in terms of required behavior. However in terms of the grammar (associativity and precedence), `f x (g a b (h c d))` and `f x @@ g a b @@ h c d` are strictly the same. – Chris Vine Apr 22 '20 at 11:25
  • @ChrisVine Since the order of evaluation of function arguments is unspecified, it exists an evaluation order with ````(g a b (h c d))```` which is impossible with ````((g a b) (h c d))````. That is ````a```` then ````(h c d)```` then ````b````. Which is obviously impossible with ````((g a b) (h c d))````. So i think they are not equivalent. Moreover If you take a look at the parsetree produced by ````f x @@ g a b @@ h c d```` you will see that it is closer to @JeffreyScofield's version. It's easy to verifiy by converting to the prefix notation: ````(@@) (f x) ((@@) (g a b) (h c d))```` – ghilesZ Apr 22 '20 at 11:51
  • Let us [continue this discussion in chat](https://chat.stackoverflow.com/rooms/212258/discussion-between-ghilesz-and-chris-vine). – ghilesZ Apr 22 '20 at 12:17
  • @ghilesZ, since Ocaml parses both into the same AST, there is no difference. – Andreas Rossberg Apr 24 '20 at 10:19
  • @AndreasRossberg there are three different versions: (1) f x @@ g a b @@ h c d (2) f x (g a b (h c d)) and (3) f x ((g a b) (h c d)). No pair among these three versions is parsed by OCaml in the same way. (1) gives an AST with 5 applications (one of wich is partial). (2) gives an AST with 3 applications (all complete). (3) gives an AST with 4 applications(one of which is partial). I think it's best to use the chat if you want to continue this discussion. – ghilesZ Apr 24 '20 at 14:29
4

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))
Anthony Scemama
  • 1,563
  • 12
  • 19
  • In the pipe case I could simply put the functions in reverse: `Array.map ... String.concat "\n"` to `String.concat "\n" ... Array.map` rigth? But the pipe way is just easier to read? – Guerlando OCs Apr 22 '20 at 05:06