Lecture thumbnail 0:00 / 0:00 So I want to talk about the maybe monad.
And this monad got kind of internalized in C-sharp six with the question mark dot operator.
But in actual fact, the maybe monad is only part of something bigger, something more complicated that
you yourself can build and thereby get additional benefits.
So this is what this particular demo is all about.
Okay.
So let me show you first of all, the kind of motivating example of the kind of problems that the maybe
monad and indeed the question mark dot operator tries to solve.
So let’s build a maybe monad demo.
So I’ll just stick a main in here.
And now let’s suppose that we have a class called person.
So I’ll have person here and person might have public address address.
Okay, that’s wrong kind of completion address like so it might be a property.
And then of course this would be an additional class somewhere and inside address you might have additional
nullable things like for example, you might have a post code.
So now the question is, well, let’s suppose we have some piece of API which tries to get the post
code out of a person.
So you have a you have some sort of method.
Method with an M, and this method takes a person and then you try to typically get the get the postcode,
assuming that none of the constituent parts are know.
So before C sharp six, the way you would write it is something like the following.
You would say string postcode equals null and then you would then check if p is not equal to null and
p dot address is not equal to null.
And actually let’s change it from null to unknown.
That would be the default value.
So the postcode is unknown by default unless you have you jump through all the hoops you say p dot address
dot postcode not equal to null and that’s the way you can assign it.
That’s where you say postcode equals p dot address dot postcode.
Now this time the time when we did this kind of thing is now gone in C sharp six.
You no longer write this kind of code because you can now write something like postcode equals P question
mark, dot address, question mark, dot postcode.
So this is what you can do right now and it works just fine.
Unfortunately, in the real world, in between those different null checks in between this check and
that check, you might also have additional code, in which case your null operator, your question
mark dot operator is completely useless.
So let me show you the kind of kind of stuff you typically encounter.
So just as an example, so you would have string postcode and then you would say something like, Oh,
okay, if P is not equal to null, then I want to check some additional conditions.
Like let’s say I want to check that the person has a medical record and their address is not null.
So we say something like if has medical record.
P I’m just going to generate this method quickly so that we have something to work with.
So if has a medical record like so and the person’s address is not equal to null, then we proceed with
something else even more complicated.
For example, we have another function which is called check address and we check the person’s address.
We verify that it’s not null.
So we perform the check on the address, which is once again another method that you would have typically.
And then after that, if p dot address dot postcode is not equal to null, that’s when you would return.
You would maybe set the postcode to p dot address dot postcode.
Let’s have it as a post code.
Dot to string.
Maybe if it’s not the string originally.
Otherwise in the else condition you would say post code equals unknown.
So as you can see, we’ve gone outside the realm of the maybe monad because all the maybe monad really
does is it checks for null.
It checks for the presence or absence of an object.
And I’ve messed things up here.
I’ve added quite a bit more information.
And so we would need to construct some different kind of APIs for processing that.
But luckily we can do exactly that using the kind of stuff that we used to do for the maybe monad in
C sharp five days, and that is to build lots of extension methods.
So I’m going to build a class called maybe now whenever we have a check for presence or absence of something
I’m going to call it with, and it’s actually an extension method.
So we would write something like public static t result with, and the template arguments here are t
input and t result.
These are the generic arguments.
Then we have to input which is an object to input like so.
And in addition we have an accessor function.
So we have a function which takes this object the input.
And returns a result that is our evaluator function.
And we have a couple of constraints.
So we say that T result has to be a class and we also specify that T input has to be a class as well
because both of these have to be nullable and for that they need to be classes.
They cannot be value types.
So if the current context of the whole evaluation chain that we have is already null.
Then we return null.
Otherwise we return the evaluator function.
On the object.
So what we’re doing here is we’re really doing a continuation passing style kind of interface, but
we’re doing it with extension methods.
So now if your object is null, then nothing is going to happen and the entire chain of calls is not
going to happen.
Otherwise you call the evaluator and the evaluator just gives you the next element of the chain.
So what this means is that instead of writing all of this, which I’m going to comment out, of course
we can do something different.
For example, in terms of checking for Null, you could have checked, you could have written something
like string post code equals person dot where or with rather with x goes to x dot address with x goes
to x dot post code.
So this would be a safe way of accessing each of the inner elements.
And so we’re kind of internalize the idea of drilling down with this new with operator that I’ve built.
But remember, it’s not all about null.
It’s not all about presence or absence.
So Null is part of the maybe monad, but we can do other things in addition to just drill down into
an object.
And here, for example, we have a bunch of ifs so we can add additional operators to our demo to support
all of this.
So the operators that I want to add, for example, let’s add the if operator.
So in this case we’ll have public static t t input because nothing changes here.
So it’s called if there is only one T input generic argument.
So we have this t input object that we’re trying something on.
Then we have a function which returns a boolean.
So that’s func t input bool I’ll call it once again evaluate or and that is pretty much all that we
need.
Once again, we put the constraint that T input is a class.
And here if the current context is null.
So if the object being passed in is null or the chain of objects, we simply return null.
Otherwise we perform a check.
So if the evaluator gives us a true if evaluator on the object gives us a true, then we return null.
Otherwise?
Well, actually, it’s the other way around.
We return the object, otherwise we return null.
So what does this give us?
Well, this means that now, in between the with calls where we perform the implicit check, for now,
we can also add if calls.
So this chain that we’re building here is becoming a bit more interesting because now we would have
something like post code equals.
So we take P and then we put an if in here we don’t even have to check P for now because that’s done
implicitly if has medical record.
So the first part of the chain after we got the P null check, which is built automatically into the
if we have the check for P having a medical record and then after we checked it, we can do another
with and we can check that the person also has an address.
And from then on we’re working with the address.
Now let’s suppose we want to check the address.
This is just a call.
It’s not conditional or anything.
It’s not an if, it’s just a call to something.
And for that we can build yet another operator called Do So Do doesn’t change the context.
It simply invokes whatever you tell it to invoke.
So public static input.
Do input this input object and an action.
So this time it’s an action, not a func, an action on the input called action.
Once again we put the constraint in where t input is a class.
Okay, so what’s happening here is if the object is null, so if the context is null, we simply return
null.
We keep propagating the null onwards.
Otherwise we call the action on the object and we return the object.
So it’s still a fluent interface.
This entire thing is a fluent interface and as a result, the next action that we can do is we can do
specify the X and we can check address of that x, which reduces to just check address.
So once again, I can get rid of this and just say check address like so.
Okay, so that’s the next part.
And then of course we want to return something.
Now we have this fallback value of unknown.
So let’s define our last and final operator, which is going to be called result.
So result will return the result of an evaluation provided we are not in a null state and it will return
some default value if we are in a null state.
So public static t result return.
Let me get this back return.
So we have to input.
We have to result.
This is the object that we’re going to call this on.
Oh, we have the function for evaluation.
So func, which takes a input and returns a result called evaluator.
And we also have the failure value, which is a result.
So if we fail, if the context is null, we have a failure value to work with.
Once again, let’s put the constraint here where t input is a class.
Remember t result doesn’t have to be a class, it can be a value type.
And what we do here is the same as what we’ve done before.
We check for Null and we either return the actual value or the failure value.
So if the object is null, we simply return the failure value.
Otherwise we return the result of the evaluation.
So evaluate are called O.
Okay, so scrolling down here, our final call here would be return.
And here what we would do is we would access the postcode.
So we would say X, go to x dot postcode, or if there is no postcode, just put in unknown.
Okay, So what we’ve just done is we’ve redefined we’ve written all of this in terms of this nice concise
invocation chain and in terms of the benefits we have embedded the maybe monad by virtue of checking
for null.
So any time you, for example, access an address that checks for null and if it’s null, then subsequent
calls do not happen.
Nothing happens with these lambdas.
Nobody calls these lambdas if your current context is null, but if it’s not null, then you get towards
the very end and you either get the postcode or you get the value unknown.
Now, the last thing I wanted to mention is that even though here I’m playing with this idea of reference
types and reference types being nullable, you can extend this model to support value types.
So for example, if you wanted to have a width that supported value types, you could define something
like the following.
So you could have, you could have public static t result with value.
With value like so and you would say t input and t result.
So you would take t input o as well as a func which takes t input and returns t result which is once
again the evaluator.
So evaluator, but this time you would constrain it differently.
You would say where t input is in fact a struct and this way you get the same behavior without the check
for null.
So you simply return the evaluator of on the object, so to speak.
So essentially, even though the built in idea of this maybe monad is still the persistent check for
null, you could have something different.
Like for example, if you’re working with value types, you could check for -1 or 0 and stop the evaluation
of the entire chain as soon as you’ve encountered a particular value.
And this is how you go beyond the simple maybe monad.
Play Play Play Play Play Stop Play information alert