ocaml

Pipeline operator in OCaml


In my practice of programming in OCaml, I have recently discovered that the following pipeline operator is very useful to create functions.

let (>|) f g x = g (f x)

val ( >| ) : ('a -> 'b) -> ('b -> 'c) -> 'a -> 'c = <fun>

I use it to replace codes like:

fun s -> int_of_string s |> Int.hash

 - : string -> int = <fun>

By the simpler:

int_of_string >| Int.hash

- : string -> int = <fun>

The associativity works fine:

int_of_string >| float_of_int >| string_of_float

- : string -> string = <fun>

Would that be a valuable improvement of the core language, and if so, how to submit this contribution?


Solution

  • This is called function composition. In Haskell the operator is ., and in Elm and F# it's >>. These work well in pure functional languages, but unfortunately fall prey to the value restriction in languages that allow both polymorphism and side-effects, such as OCaml, and is therefore unlikely to be accepted I think.

    This example illustrates the problem:

    let f x = x (* val f : 'a -> 'a *)
    let g x = x (* val g : 'a -> 'a *)
    
    
    let h = f >| g (* val h : '_weak3 -> 'weak3 *)
      
    let a = h "foo"
    let b = h 42 (* Error: This expression has type int but an expression was expected of type string *)
    

    For details on the value restriction and the limitations of polymorphism in OCaml see the OCaml manual. But the gist of it (as I understand it) is that if there is a possibility of mutation and polymorphism being mixed, type variables can't be fully generalised and will be left as "weak" type variables, which might be determined at some later point, such as when it's first used, but cannot be polymorphic. For the purpose of a composition function, that unfortunately makes it quite restrictive and confusing to deal with.

    OCaml does however relax the value restriction a little bit, and allows such functions if they are syntactic functions. Which means you can do

    let h s = (f >| g) s
    

    But then you might as well just do

    let h s = s |> f |> g
    

    or

    let h s = g @@ f s