10

I'm converting some F# code to OCaml and I see a lot of uses of this pipeline operator <|, for example:

let printPeg expr =
    printfn "%s" <| pegToString expr

The <| operator is apparently defined as just:

# let ( <| ) a b = a b ;;
val ( <| ) : ('a -> 'b) -> 'a -> 'b = <fun>

I'm wondering why they bother to define and use this operator in F#, is it just so they can avoid putting in parens like this?:

let printPeg expr =
    Printf.printf "%s" ( pegToString expr )

As far as I can tell, that would be the conversion of the F# code above to OCaml, correct?

Also, how would I implement F#'s << and >> operators in Ocaml?

( the |> operator seems to simply be: let ( |> ) a b = b a ;; )

Guy Coder
  • 24,501
  • 8
  • 71
  • 136
aneccodeal
  • 8,531
  • 7
  • 45
  • 74
  • Just out of curiosity, I spend a lot of time translating from OCaml to F# because of the large sets of open code in OCaml, the ease of translating to F#, being able to use Visual Studio, and support here, what benefits do you gain by going from F# to OCaml? – Guy Coder Jan 19 '13 at 14:10
  • 1
    @GuyCoder: First off, I'm not on Windows. I develop on Linux. I suppose I could use Mono to develop F# on Linux, but I'm more familiar with OCaml & it's tool chain and I guess I don't want to depend on the Mono runtime. I also like some of the features that OCaml has that F# lacks like functors & first class modules. Yes, I've noticed that there is lots of info about going from OCaml to F#, but not so much going the other way. We need some kind of compatibility library/layer. – aneccodeal Jan 19 '13 at 18:12
  • Did you know about [FSharp.PowerPack.Compatibility.dll](https://github.com/fsharp/powerpack/tree/master/src/FSharp.PowerPack.Compatibility)? It doesn't cover everything but it does help. I am currently translating ML code with functors, being my first time with functors I see the appeal, and I really grew to love the [time travel](http://caml.inria.fr/pub/docs/manual-ocaml-4.00/manual030.html) feature of debugging OCaml. OCaml has a lot to offer, and I would recommend it just as often as F#. :) – Guy Coder Jan 19 '13 at 18:51
  • Also, F# could really benefit from a preprocessor like Camlp4. Based on what I see in the forums, I don't think that will ever happen from within Microsoft. F# has other ways to do some of those fetures, but you really have to learn a lot of F# to make the translations using advanced Camlp4. – Guy Coder Jan 19 '13 at 18:59

4 Answers4

16

why they bother to define and use this operator in F#, is it just so they can avoid putting in parens?

It's because the functional way of programming assumes threading a value through a chain of functions. Compare:

let f1 str server =
    str
    |> parseUserName
    |> getUserByName server
    |> validateLogin <| DateTime.Now

let f2 str server =
    validateLogin(getUserByName(server, (parseUserName str)), DateTime.Now)

In the first snippet, we clearly see everything that happens with the value. Reading the second one, we have to go through all parens to figure out what's going on.

This article about function composition seems to be relevant.

So yes, in a regular life, it is mostly about parens. But also, pipeline operators are closely related to partial function application and point-free style of coding. See Programming is "Pointless", for example.

The pipeline |> and function composition >> << operators can produce yet another interesting effect when they are passed to higher-level functions, like here.

Community
  • 1
  • 1
Be Brave Be Like Ukraine
  • 7,596
  • 3
  • 42
  • 66
  • -1 "because the functional way of programming assumes threading a value through a chain of functions". If that were true then OCaml would have had such operators before F#. Also, your examples are not equivalent because you've uncurried and added superfluous parentheses. `validateLogin DateTime.Now (getUserByName server (parseUserName str))`. – J D Jan 20 '13 at 12:08
  • 6
    @JonHarrop Please read carefully. I have intentionally made `date` a **second** parameter to demonstrate usefulness of `<|` operator. – Be Brave Be Like Ukraine Jan 20 '13 at 12:31
15

Directly from the F# source:

let inline (|>) x f = f x
let inline (||>) (x1,x2) f = f x1 x2
let inline (|||>) (x1,x2,x3) f = f x1 x2 x3
let inline (<|) f x = f x
let inline (<||) f (x1,x2) = f x1 x2
let inline (<|||) f (x1,x2,x3) = f x1 x2 x3
let inline (>>) f g x = g(f x)
let inline (<<) f g x = f(g x)
ildjarn
  • 62,044
  • 9
  • 127
  • 211
12

OCaml Batteries supports these operators, but for reasons of precedence, associativity and other syntactic quirks (like Camlp4) it uses different symbols. Which particular symbols to use has just been settled recently, there are some changes. See: Batteries API:

val (|>) : 'a -> ('a -> 'b) -> 'b

Function application. x |> f is equivalent to f x.

val ( **> ) : ('a -> 'b) -> 'a -> 'b

Function application. f **> x is equivalent to f x. Note The name of this operator is not written in stone. It is bound to change soon.

val (|-) : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c

Function composition. f |- g is fun x -> g (f x). This is also equivalent to applying <** twice.

val (-|) : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b

Function composition. f -| g is fun x -> f (g x). Mathematically, this is operator o.

But Batteries trunk provides:

val ( @@ ) : ('a -> 'b) -> 'a -> 'b

Function application. [f @@ x] is equivalent to [f x].

val ( % ) : ('a -> 'b) -> ('c -> 'a) -> 'c -> 'b

Function composition: the mathematical [o] operator.

val ( |> ) : 'a -> ('a -> 'b) -> 'b

The "pipe": function application. [x |> f] is equivalent to [f x].

val ( %> ) : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c

Piping function composition. [f %> g] is [fun x -> g (f x)].

lukstafi
  • 1,883
  • 15
  • 22
5

I'm wondering why they bother to define and use this operator in F#, is it just so they can avoid putting in parens like this?

Excellent question. The specific operator you're referring to (<|) is pretty useless IME. It lets you avoid parentheses on rare occasions but more generally it complicates the syntax by dragging in more operators and makes it harder for less experienced F# programmers (of which there are now many) to understand your code. So I've stopped using it.

The |> operator is much more useful but only because it helps F# to infer types correctly in situations where OCaml would not have a problem. For example, here is some OCaml:

List.map (fun o -> o#foo) os

The direct equivalent fails in F# because the type of o cannot be inferred prior to reading its foo property so the idiomatic solution is to rewrite the code like this using the |> so F# can infer the type of o before foo is used:

os |> List.map (fun o -> o.foo)

I rarely use the other operators (<< and >>) because they also complicate the syntax. I also dislike parser combinator libraries that pull in lots of operators.

The example Bytebuster gave is interesting:

let f1 str server =
  str
  |> parseUserName
  |> getUserByName server
  |> validateLogin <| DateTime.Now

I would write this as:

let f2 str server =
  let userName = parseUserName str
  let user = getUserByName server userName
  validateLogin user DateTime.Now

There are no brackets in my code. My temporaries have names so they appear in the debugger and I can inspect them and Intellisense can give me type throwback when I hover the mouse over them. These characteristics are valuable for production code that non-expert F# programmers will be maintaining.

J D
  • 48,105
  • 13
  • 171
  • 274
  • 1
    -1: You are answering questions that haven't been asked. It's not about personal preferences or if we find pipelining and composition operators useful. Also, it is not about replacing `|>` with a series of `let` instructions like you did reviewing my answer. Your point about type inference, however, seems to be important, so if you consider cleaning up your answer it would look more constructive. – Be Brave Be Like Ukraine Jan 20 '13 at 12:27
  • 5
    +1 Please keep giving the extra info in the answers. There are many times I ask a question because I need to know something and what I ask is not what I need because I don't know exactly what I need. If I did I wouldn't be asking as many questions. The usefulles of |> with type inference took me some time to understand and John notes it here for free. Readers are free to ignore it. This is not a go against bytebuster :) but to say that I still have a problem with the ethos of SO. Is it a sight to give out fish or teach people to fish? – Guy Coder Jan 20 '13 at 14:10