Bind
Use an already-Optional function in a chain.
What's the difference?
Like Map
, the Bind
functions receive the value of the input Option<T>
if it's a Some<T>
, and are bypassed if it's a None<T>
.
However, there are two important differences:
Bind
functions return anOption<T>
(Map
functions returnT
which is wrapped bySome
).Therefore they are expected to handle their own exceptions - one of our key principles is the contract that if a function returns
Option<T>
its exceptions have been handled. (The method signature therefore does not have aHandler
in it.)
That said, you can be naughty because your binding function is still wrapped in a try..catch
block. However all exceptions caught in that block will return None<T>
with a Msg.UnhandledExceptionMsg
as the Reason.
Bind<U>(Func<T, Option<U>>) -> Option<U>
Bind<U>(Func<T, Option<U>>) -> Option<U>
Bind
does a switch
and behaves like this:
if the input
Option<T>
isNone<T>
, returnNone<U>
with the original Reasonif the input
Option<T>
isSome<T>
...get the Value
T
use that as the input and execute
Func<T, Option<U>>
, then return the resultcatch any unhandled exceptions using
DefaultHandler
See it in action here:
This is identical to the previous snippet at the end of the Map section - except there are no exception handling options. So, if you change x / 2
to x / 0
you will get an UnhandledExceptionMsg
.
Bind comes into its own in more complex scenarios, for example data access:
If db.GetCustomer()
fails, the next two operations are skipped, AuditSwitch
is run with the none
option, and a None<OrderPlacedModel>
is returned with the reason from db.GetCustomer()
.
You can of course also return Tuples instead of single values, and C# is clever enough to give Tuples automatic names now as well:
Adding it all together
In the next section we will actually make a simple test app with everything you need to play around with Map
and Bind
. Before then here is an example of what this can look like in practice:
Here we have two potentially problem-ridden methods - returning a complex SQL query from a given input and executing a database query - that are now pure functions: notice they are declared static
. This is best practice, think of it like Dependency Injection but functional. The functions require no state, and interact with nothing else, so given the same input they will always return the same output.
This means they can be tested easily, and once they work correctly they will always work in the same way so you can rely on them.
You will also notice the use of the -Async
variations of Map
and Bind
- we will come to these later, but for now note that although x.ToList()
is not doing anything asynchronously, because the input is a Task<Option<T>>
we must use the -Async
variant. This leads to another of our key principles: once we are in the async world we stay in the async world.
Last updated