The only difference between this function and one that simply unwraps an option safely (ie. with a ~default argument) is that here the default argument isn’t evaluated right away but is instead a function to be called if needed. This is why Containers calls this function get_lazy (corresponding to its get_or, which preceded the stdlib’s value but has the same functionality). A similar name for this function that matches the stdlib’s terminology would be value_lazy.
Want something like this? with monadic syntax support, it might be nice ?
module OM = struct let (>>=) v f = match v with Some _ -> v | None -> f() end ;;
module OM :
sig val ( >>= ) : 'a option -> (unit -> 'a option) -> 'a option end
# OM.( (Some 2) >>= (fun () -> Some 1)) ;;
- : int option = Some 2
# OM.( None >>= (fun () -> Some 1)) ;;
- : int option = Some 1
The good thing about the monadic way of doing this [and I’m certainly not a fan of monads] is that you get the short-circuit behaviour of conditional-or. if you’re going to put something into stdlib, it seems like that’s important, no?
Thanks @Chet_Murthy for suggesting the Monad Way. I’ve tried the let+ and it works but I will not call it an improvement.
Judge for yourself
let ( let// ) v f = match v with Some _ -> v | None -> f ();;
let// x = Some 1 in Some 3 (* x is Some 1 *);;
let// x = None in Some 3 (* x is Some 3 *);;
let f () = print_endline "********"; Some 9;;
let// x = Some 1 in f () (* x is Some 1 *);;
let// x = None in f () (* x is Some 9 and some stars are printed *);;
I’ll probably stick with pattern matching for now.
But now I’d like to have something like that in Stdlib
module Option
...
let bind o f = match o with None -> None | Some v -> f v
..
module Syntax = struct
let ( let+ ) = bind
end
end
Someone on here some time ago came up with a let|| (let-or) operator which I noted down at the time because it seemed quite interesting. It will accept the first option which does not evaluate to None. You can make the default the last value:
let (let||) opt f =
match opt with
| Some _ -> opt
| None -> f ()
So you could have this:
# let x =
let|| () = Some 10 in
let|| () = Some 20 in
Some 30;;
- : val x : int option = Some 10
let x =
let|| () = None in
let|| () = None in
Some 30;;
- : val x : int option = Some 30
The fact that the (let||) operator automatically puts everything after the first binding into a thunk, and so on thereafter, is the nice part I think, because it gives you proper short-circuiting. I think that outweighs the slight complication of the syntax.
I’m feeling this sort of … disquiet, that there’s no way to get this same functionality using infix operators. Let is fine for sure, but it seems unfortunate that there isn’t some way to have an infix operator that implicitly lazy-i-fies its right-hand-side argument, so that we can express short-circuit-ing without using let.