The common signature of functions declarations in a module is when the last parameter has a type of the main state (Module.t). Like it is in the 'List' module. This form opens the ability to use '|>' operator like:
[1;2;3] |> List.filter ((>)2)
|> List.map ((-)1)
|> List.fold_left 0 (+)
But function 'bind' in 'Option' module doesn't follow this form. It has 'Option.t' parameter as the first
val bind : 'a option -> ('a -> 'b option) -> 'b option
But ok, I can change it. I declared function 'opt_bind' with the reverse order of parameters.
let opt_bind = Fun.flip Option.bind
But this one doesn't work. And the following code was compiled with the following error
type a = A of int
type b = B of int
let f x = Some (A x)
let g (A x) = Some (B x)
let opt_bind = Fun.flip Option.bind
let result =
(Some 42) |> opt_bind f
|> opt_bind g
|> opt_bind g ^
Error: This expression has type a -> b option but an expression was expected of > type int -> a option. Type a is not compatible with type int
The same situation with
let result =
let x = opt_bind f (Some 42) in
let x = opt_bind g x in
x
Even after I had noticed all types I still have the same problem.
let f : int -> a option = fun x -> Some (A x)
let g : a -> b option = fun (A x) -> Some (B x)
let opt_bind : ('a -> 'b option) -> 'a option -> 'b option =
Fun.flip Option.bind
let result : b option =
let x : a option = opt_bind f (Some 42) in
let x : b option = opt_bind g x in
x ;;
But
let result =
let x = Option.bind (Some 42) f in
let x = Option.bind x g in
x
works fine.
Why does 'opt_bind' have the wrong type expectation for 'g' as if 'opt_bind' isn't generic?
How to use 'bind' with '|>' notation?
Your problem is that your defintion of opt_bind
isn't polymorphic enough. Because you define it as an application (of Fun.flip to Option.bind), it can't be made polymorphic due to the value restriction.
If you define it like this:
let opt_bind a b = Fun.flip Option.bind a b
or, equivalently, like this:
let opt_bind a b = Option.bind b a
then things will work.
If you ask for the type of your opt_bind
definition you'll see the problem:
# let opt_bind = Fun.flip Option.bind;;
val opt_bind :
('_weak3 -> '_weak4 option) -> '_weak3 option ->
'_weak4 option = <fun>
The "weak" type variables are telling you that the resulting function is not polymorphic.
The essential difference is that Fun.flip Option.bind
is syntactically an application (a function call). Such an expression can't be made polymorphic. The two alternate forms define bind_opt
as a lambda (a function value), which is a syntactic "value" in the terminology of the value restriction.
The value restriction is required to make sure that polymorphic functions are sound (i.e., that they don't allow inappropriate operations on values).
My reference of choice for the value restriction (especially as implemented in OCaml) is this paper: Relaxing the Value Restriction, Jacques Garrigue