Zen icon indicating copy to clipboard operation
Zen copied to clipboard

Add additional `Option` methods

Open alberdingk-thijm opened this issue 2 years ago • 3 comments

Add Option equivalents of the following System.Linq.Enumerable methods:

  • Intersect: a kind of eager Boolean "and" over 2 options. Returns the second option unless the first is None.
  • Union: a kind of eager Boolean "or" over 2 options. Returns the first option unless it is None.
  • SelectMany: a chainable version of Select akin to Enumerable.SelectMany. The supplied function returns an option rather than a bare type, and the result type is the function's result type.
  • SomeOrDefault: a kind of Enumerable.Concat over an option and an option-generating thunk. If the option is None, call the thunk to generate a default option.

All names of these new methods are open to revision. These are less common methods and their names vary in other programming languages. Alternatives might be:

  • Option.Intersect --> Option.And
  • Option.Union --> Option.Or
  • Option.SelectMany --> Option.Bind or Option.AndThen
  • Option.SomeOrDefault --> Option.Concat or Option.OrElse

SomeOrDefault is called or_else in Rust and C++23. SelectMany is variously called and_then (Rust, C++23, Elm), bind (OCaml), or flat_map (Nim, Swift). Intersect is called and in Rust, and Union is called or in Rust.

alberdingk-thijm avatar Dec 08 '23 15:12 alberdingk-thijm

@rabeckett Feel free to suggest alternative names. One option (ha ha) is to stick very closely to System.Linq.Enumerable.

  • Option.Intersect for the "and" of two Options. A bit unclear as a name perhaps, since two Option.Somes with different values don't produce Option.None: Option.Some(3).Intersect(Option.Some(1)) == Option.Some(1).
  • Option.Union for the "or" of two Options. Again, a similar problem to Option.Intersect, as "union" does not suggest that, when given two Option.Somes, the result is the first value: Option.Some(3).Union(Option.Some(1)) == Option.Some(3).
  • Option.Concat instead of Option.SomeOrDefault. Somewhat intuitive since it respects the fact that Enumerable.Empty is an identity: Enumerable.Empty<int>().Concat(enumerable) == enumerable and Option.None<int>().Concat(thunk()) == thunk(). On the other hand, SomeOrDefault tells you more about what it actually does.
  • Option.SelectMany for a flat-map over an Option. "Many" looks a little strange in this context, since we're not projecting multiple enumerables into one, but that's an unfortunate consequence of C#'s choice of names.

Names that might be less aligned with Enumerable but more suggestive of the methods' purposes would be Option.And, Option.Or, Option.SomeOrDefault and Option.AndThen, respectively.

alberdingk-thijm avatar Dec 08 '23 16:12 alberdingk-thijm

Very nice.

In terms of naming, I find the rust naming convention to be fairly sensible.

Option.Intersect -> Option.And Option.SelectMany -> Option.AndThen Option.Union -> Option.Or Option.SomeOrDefault -> Option.OrElse

A few other useful variants in rust, that are similar to and and and_then: Option.UnwrapOr Option.UnwrapOrElse

rabeckett avatar Jan 04 '24 17:01 rabeckett

Very nice.

In terms of naming, I find the rust naming convention to be fairly sensible.

Option.Intersect -> Option.And Option.SelectMany -> Option.AndThen Option.Union -> Option.Or Option.SomeOrDefault -> Option.OrElse

A few other useful variants in rust, that are similar to and and and_then: Option.UnwrapOr Option.UnwrapOrElse

Sounds good, I've updated the naming scheme. Option.UnwrapOr is equivalent to Option.ValueOrDefault as far as I can tell; we could change the name to UnwrapOr if you like. I can add in Option.UnwrapOrElse too.

alberdingk-thijm avatar Jan 08 '24 18:01 alberdingk-thijm