Hi! I just made a PR to Belt to add a new Option function. I was asked to get some wider input, which is a good idea, so I’d appreciate your thoughts on 2 things – is this a good addition? And which naming do you prefer? To save you navigating, I’ll paste the PR description below, followed by a naming poll.
Edit: here’s what you’d be able to do if the PR gets merged:
let value = option
->Option.orElse(alternativeOption)
->Option.getWithDefault("string")
In terms of naming, I followed the convention from Scala: getOrElse is to orElse as Belt.Option.getWithDefault is to Belt.Option.withDefault. An alternative would be to use orElse, that might be better than using the word “default” for an optional value.
I think ocaml’s value Is the equivalent of rescript’s getWithDefault? I don’t think ocaml has an equivalent of the function I’ve added in the MR, which probably explains why it wasn’t added to Belt originally.
I called this selfOr in my local stdlib. I also made the alternate value lazy, with a matching selfOrStrict that just takes a plain option; in the project I work on alternate values are rarely cheap to calculate. But that’s personal preference
So, orElse is essentially <|> from Haskell? I’m 100% in favor of having it. I was recently caught off guard that it wasn’t already in there.
I think the naming is fine. I actually want to change getWithDefault more. It’s kind of a verbose function name for something that is inevitably going to be used all the time.
I agree, although we can’t really change it now, I use the OCaml style for this in my stdlib because I really like named arguments. The PR example, in my codebase, becomes:
let value = option
->Option.selfOr(alternative)
->Option.value(~default="string")
Does this use-case also relate to the Maybe/Option monad, when you string along multiple option values? Or am I totally wrong? Dunno if ReScript supports the let* notation.
ReScript already has an easy concept for thunks, called lazy. This is why my selfOr method is lazy by default - and I have one for the unwrap case as well:
let selfOr: (option<'a>, Lazy.t<option<'a>>) => option<'a>
let orLazy: (option<'a>, Lazy.t<'a>) => 'a
Lazy is a fun concept, code is written as if the value is returned immediately but computation is suspended (it compiles to a function in JS so it’s almost exactly equivalent to your proposal).
let value = option
->Option.selfOr(lazy(someReallyExpensiveOperation())
For another comparison, this is called first_some in Jane Street’s Base library.
Not commenting one way or another on the name, but if you are interested in some uses “in the wild”, you can check out a bunch of examples in this sherlocode search. (Examples in OCaml, but you will get the idea.)
The code with lazy will work not as we used to with map, flatMap. If it’s called the second time it will return the first result, without recomputing it, even though it could have changed
Slightly off-topic, as not in “what goes in belt”. One could consider overloading || which just happens to already have the correct semantics based on the runtime representation of optionals.