-
-
Notifications
You must be signed in to change notification settings - Fork 0
Chains
- Home
- Some and None
- Chains
- Map
- Bind
It's all very well creating a lovely Maybe<T>
with Some<T>(T) -> Maybe<T>
, but what do you do with it? Do you really want to have code like this everywhere:
var maybe = F.Some(42);
if (maybe.IsNone(out var reason))
{
// do something
}
else if (maybe.IsSome(out var value))
{
// do something else
}
else
{
// what do we do here??
}
Of course not! That's a mess.
Instead we use functions to chain Maybe<T>
results together, so the flow looks something like this:
Some<A> ->> Link<A, B> ->> Link<B, C> ->> Link<C, D> ->> Some<D>
If all the links work, each receives value A / B / C, returns value B / C / D, and we end up with Some<D>
. This is the basic concept behind Scott Wlaschin's Railway Oriented Programming. It's a different way of coding to OO / C#, because instead of writing a method that does a lot of things, we write several small functions, each of which does one thing, and then we chain them together. (In F# this is called composition, but we can't do that in C# so instead we use a 'fluent' style syntax.)
This is great - but what if something goes wrong? Then we want the flow to look something like this:
something goes wrong in this link
/
/
Some<A> --> Link<A, B> --> Link<B, C> --> Link<C, D> --> None<D>
\ /
\ /
- - - - - - - - - - - - - - - - -
short circuit the other links
Something goes wrong in the first link, and instead of a Some<B>
to pass a value to the next link in the chain, we get a None<B>
(with a reason message of course). All the next links in the chain are skipped, the None<B>
is transformed into a None<C>
and then to a None<D>
(preserving the original reason message), and returned.
The chaining is handled by two function groups: Map
and Bind
. These do the switching for you:
- if the input is
Some<T>
they pass the value to your function - if the input is
None<T>
they skip your function and returnNone<TReturn>
(the next type), preserving the reason message