Optional and orElse: design feedback/critique?

Johannes Loher johannes.loher at fg4f.de
Sat Jul 27 16:27:53 UTC 2019


Am 27.07.19 um 15:17 schrieb aliak:
> Hi,
> 
> Can I ask for feedback on what people expect an optional/maybe type to
> have, and how to behave. There have been a number of discussions on the
> forums about it so far, and attempted PRs in phobos. And I currently
> maintain one that I've been using in-house very happily (backend server
> with vibe-d). I want to nail it down further and get to a version 1.0.0
> so feedback and comments would be highly appreciated.
> 
> Optional semantics:
> ===================
> 
> So first, the semantics of Optional!T (these may or may not already be
> implemented):
> * Is at least a forward range
> * Equality with itself, T, and sentinel-type "none", which checks if
> optional is empty or not
> * Assignment to T, `none`, or Optional!T
> * Includes type constructors some(x) and no!T
> * Forwards operators to T if T implements those operators
>   E.g. some(1)++ and no!int++ both work
>   E.g. If T has an opCall(...), then some(x)(...) and no!T()(...) both work
> * Null pointers are valid objects, so some!(int*)(null) is non empty
> * Null classses and interfaces are empty, so some!Class(null) is empty
> 
> Optional utilities:
> ===================
> 
> Two utilities are included. Optional chaining and a match function.
> Optional chaining allows you to go through an object's hierarchy and get
> an optional back at the end:
> 
> oc(obj).property.function()
> 
> If obj is none, then the end result is no!T where T is the return type
> of function. Else it has the actual value if the chain goes all the way
> though. This will also work if property is in turn an Optional itself.
> Secondly, the chaining function is also provided for NullableT and
> reference type.
> 
> The match function takes two handlers (lambda aliases) and calls the
> correct one based on if the Optional!T is empty or not.
> 
> orElse semantics:
> =================
> 
> orElse will either get the front of a range or the range itself
> depending on the alternative value and also works on reference types. So:
> 
> Range!int range1, range2;
> range1.orElse(range2) // returns range1 if range1 is not empty, else range2
> range1.orElse(8) // returns front or range1 if non empty, else 8.
> 
> In essence, it also coalesces. I was on the fence on this, but it's
> turning out (again in our project) to be very convenient. I'm
> considering doing this for primitive types like int, float, or anything
> that can be cast to bool (while taking things like NaN in to account for
> e.g.)
> 
> So orElse works on a number of types and these are each types's semantics:
> 
> * If T is a reference type, val1.orElse(val2) will return val1 if (val1
> !is null), else val2
> * If T is a range, the example above shows the semantics
> * If T is an Optional!U then it is treated separately in order to work
> with const Optional!U. The value returned by orElse is determined by the
> alternative value. If it is a U then optional.front will be returned, if
> it is an Optional!U then an optional will be returned. So the same
> semantics of range.
> * If T is Nullable!U, then isNull is checked. If it is false and
> alternative value is a U, then nullable.get is returned. If T is another
> Nullable, then the return type is a Nullable.
> 
> I may have forgotten some stuff but I think that's everything.
> 
> Thanks in advance!
> 
> PS: I realize an obvious first comment is "what about Nullable!T". A
> number of reasons: 1) it doesn't have a range interface 2) the reference
> semantics are not desirable for me and writing generic code is awkward
> (https://github.com/aliak00/optional#what-about-stdtypeconsnullable), 3)
> alias this, although deprecated, breaks it completely, 4) maintaining a
> dub package feels much more productive.

orElse currently has an overload that takes a callable that returns a
fallback value as template parameter. I think something like this is
needed, but it might be better to give it a different name (in Java,
Scala etc. this is called orElseGet). Other convenience utilities might
be nice, e.g. ifPresent, ifPresentOrElse, orElseThrow etc. (take Java's
Optional or Scala's Option as reference). These are all very easy to
implement and I have already done so several times when using your library.

I also think that your current orElse semantics are a bit too
complicated: There is simply too much stuff (with slighty differing
behavior) in that one single function (I know, it is several overloads,
but the user does not care). E.g. I think the overload that takes
another range (or Optional or Nullable) should be a seperate function
because it actually has different semantics. All overloads should have
the same general behavior, in my opinion. In Java, this function is
called "or".

Otherwise, I actually quite like the semantics of your library (and I
use it regularly). Thanks for your work!


More information about the Digitalmars-d mailing list