a monad is a well-known Concept in functional programming languages like hasal but what is it well it's a monoid in the category of endofunctors that respect certain rules and has operations to chain computations together this may sound like incomprehensible blabbering to you but my goal is that by the end of this video you know exactly what it means I think it's important that we as programmers sometimes dive into the theory behind programming it helps us gain a broader understanding of why things are as they are it also helps us impress our loved ones at social
events assuming they're interested in category Theory this video has been on my list for a very long time it's sort of become my Nemesis when I look online for explanations about what a monad actually is it's often way too theoretical using terms that are hard to understand for the average programmer and without really diving into why this concept is useful today we're going to explore what exactly is monad and we're going to find find out if monads are useful at all Beyond functional languages like hasal be warned though I'm going to dive in pretty deep
in this video if you feel like it's going to bit too far take a break and join my free Discord server at discord. rn.c it's a really nice helpful Community now if you're ready let's dive into the rabbit hole a monad is one of the few design patterns in functional programming like I said in the beginning a monad is a monoid in the category of endofunctors what does that mean well let's start at the end with the word endo functor and for that I'm first going to look at the last part of that which is
functor so what is a functor a functor is a combination of two things it's an object a value combined with a mechanism to do something with that value now that all sounds rather abstract but if you take a look at this class that I've written right here this is an example of a funtor it has a value which gets set in the initializer and it has a mechanism to do something with that value namely applying some sort of function to the value so you see that's happening here in the body of this method called map
we're calling this function we're providing it the value that's the mechanism of doing something with the value and then map returns another functor so these are the parts that defines what a funtor is it encapsulates a value it provides mechanism for doing something with that value this case applying a function and after doing that it returns a new functor containing that process that changed value and that's exactly what you're seeing here and map is a pretty common name for that an endo functor is a specific version of a functor that does exactly the same thing
except that when it returns a functor it returns something that has the same shape as the original functor that the method was called on and by shape I mean that it's the same combination of the two things it's the Value Plus the same method for applying an operation to the value and in this case the functor class is actually an example of an endofunctor because it returns a functor which has the same shape as the original functor even though the value is different so it's really about the structure it's not about the value not even
the type if you have a function that takes a value let's say of type integer and turns it into a string then funter is still an endof funter because it returns something that has the same shape it has a value we don't care about the structure of the value we care about that it is a value and it has the same method for doing something with that value for modifying for processing that value so that's what an endo functor is here we have an example of class that's not an endofunctor this is class string functor
it gets a value stores that value and then it has a map method just like the original fun that I showed you also gets a function but here this doesn't return a string functor it returns a list functor which is another type of functor that gets a value that's stored in list so in this case what we're doing is that we're changing the functor that it returns it's not the same thing so string fun because it returns a list functor and not a string functor is not an endofunctor it's a regular functor if we go
back to the original funtor class that I showed you I've also added an example here so we start with a functor containing the value five and then if I do map add one so the add one function gets an integer and returns an integer and then I map that onto this particular functor then G the result of that map function is also a functor instance that's because map returns a funtor so the funtor returns another instance of the funter the second thing is that it preserves the structure so G is an instance of funter structure
is the same final rule of something being an endofunctor is that there is composition so if we map add one and then we map multiply by two and we take a look at the value then that's the same as mapping simply applying these two functions in in sequence directly and if we get the value that way it's also exactly the same so composing various maps in sequence is the same as simply applying the function that's the rule of composition so to recap monad is a specific type of endo functor it encapsulates a value it provides
a mechanism for doing something with that value and that mechanism Maps back to the same category so you start with a funtor you start with monad and you get another monad back as a result it also preserves the structure so you get the same monad back and it preserves composition so instead of mapping two things in sequence you can also simply call those two things immediately and then the result is going to be the same so the monad monness doesn't change composition another aspect of the monad is that it's also a monoid so what is
a monoid well it's a set of elements with a B binary operation so you combine two elements that produces a third it should be associative so the order in which you do the binary operation should matter and there should be an identity element you could should be able to pick one of the two elements as a particular value and then it should result in the same element if you take a look at this function for example that's of course very basic function but this is actually a monoid because it has it's a b operation so
it takes two elements returns a third which is the sum of X and Y it's also associative because it doesn't matter if we do X Plus y or y + x and there's also a unit element zero because we if if we add zero to X then we get X so this is a monoid and a monat is a monoid in the domain of endofunctors so monat is an endofunctor so it encapsulates a value it provides a method for doing some computation on that value and when it runs that computation it returns another Mona that
has exactly the same structure but because it's a monoid it also has these two aspects of having a unit type operation and being associative so here's an example of a monut which is only slightly different from the funtor example that we've seen before so I have an initializer that gets a value I have a map method in this case I'm calling that bind and this simply applies a function to a value but instead of turning the result of the function here into a monat like what I did here I turned it into a funter so
I'm not doing that here actually here I'm expecting that the function is going to provide a monad already as a result and then bind simply calls the function and then Returns the result we also have a unit mechanism so we can take any value and we can turn it into a monut well simply by calling the initializer you could even remove the static method because it's already there in the initializer now I did one thing here which is that I use generics so that if I have a monad like this I can basically use it
for any type that I like so I Define two type bars T and U monad is a generic that relies on type T and that's an in SL so we get a value of a type T and then we have the bind method which has a function that expects a value of T and that then returns a monad of Type U so this also shows that this function doesn't necessarily have to return something that has the same value type it can be a different type and then finally the unit static method takes any value and
turns it into a monat and now we have a monat and since a monat is a monoid it has to adhere to these rules of on the one hand identity so this should be a unit or initializer method that's what see here but also the associativity so we should be able to bind functions in sequence and that should give us the same result as applying those functions directly so here I have main function showing again how this works so I've created now a monat with value five and then first I check for the identity so
you can do that on the left side and on the right side so in this case I'm binding the monad with a function that turns add one of X into a monad and the value is of that is exactly the same as simply calling the function with a value five so that's the identity part right identity is the other way around so if I do bind on the unit and then take the value from that that should be the same as the monad value itself so this checks for the identity second part is associativity so
I should be able to bind F then bind G and that should be the same as binding a function that binds the function and then binds G it's all a bit complicated and here you can see I added the assert to check this but it looks a bit complicated because of course there's a bunch of Lambda functions and monads but when I run this you see that actually the values in the end are the same so it doesn't output anything in other words these asserts they pass so again this all looks a bit complicated but
the whole idea is that a monat is a combination of a value that's encapsulated plus a mechanism changing the value and it has certain properties so when you do that processing on the value you get back another monat of the same structure and if you do multiple of these operations in sequence using the monat bind method then that's the same as applying those operations directly on the value without using the monad that's the associativity rule and why is this important and useful well because of all those rules surrounding monads it means that you now can
encapsulate side effects in a monad object because we know that the structure stays the same we know that we're not changing the way that the order of applying operations changes so that means that we can capture side effects in the Monet object in the value that is part of the Monet object and that's the whole idea of a Monet by the way if you like diving into more advanced features like decorators context managers other Advanced features of python you might enjoy my next level python course to learn more about that check the website below now
we've seen a basic example of what a monad is why should we use that what is that useful for well a pretty common monad that you may en counter is the maybe monad and the maybe monad is a variety of this very generic monad that I showed you just now the specific thing that it does is that you can provide an optional value so value can be none and then the bind method it has a function that takes a value of the type T returns another monad right but then what it does is that it
Returns the monut if the value is none otherwise it simply applies the function to the value value like with the original monad so instead of the simple monad I had here which just always applies the function the maybe monad only does that if the value is not none and that's actually quite useful because that allows us to make sure we only apply an operation to the value if the value actually is valid is not known so here's an example of how you could use the maybe mon out so I have a safe divide function that
divides X by Y and that returns a maybe Monet type float so what it does is if y equals 0 we can't divide by Z obviously so then we return maybe n else we return x / y as regular and then what we can do is use the bind function to do safe divisions in sequence so here is a result here's an example I start with 10 then I divide by zero which is not allowed right so that should return n and then I divide by two when I run this you see that the result
is none we don't get an exception or something like that and that's because the maybe Mona takes care of routing us to the nonp part of the monab whenever we not able to divide by zero so here because I'm not able to divide by zero that results in maybe n and then this bind method call Simply returns self and doesn't do the second function call so it doesn't run this function actually which is nice because now now we will only run this function If the previous step was successful so if I change this into a
two and then I run the code again you see we get 2.5 so we have 10 / 2 / by two and I can also turn this into a zero and then you'll see we'll also get none as a result because the second division wasn't possible so that's the whole idea of the maybe Monet and this type of programming where we have these two options we have the One path and we have the path where we still dealing with a valid value is also called railroad oriented programming because we have these two tracks that our
program is running on in that sense it's an alternative to the regular exception handling of python which works differently right with exception handling well we have our program and then if there is an exception there's a separate piece of control logic that you deal with which is the exception handling part of your program with monad with the maybe monad you have two power paths the non-path and the valid path so it's a different approach to handling errors and to show you how this all combined so here I have another example which is again the maybe
monad but I did things a bit differently so I now used pattern matching which is a new feature relatively new feature in Python that allows me to do match case statements in order to support this I needed to extend the class a bit so that I tell the maybe monat that it should match arguments on the value so that's what it should check and then I also have a match D method that compares the two values of these two monads two maybe monads then in this example I have a function that parses an integer so
we get a string value and it returns a maybe monat containing an integer so if we can turn it into an integer then we simply return that if there's a value error we return maybe none so this translates the python exception model into a montic error handling model and then I have few other functions so one checks whether the value is positive and here's another function that simply doubles the value so then what we can do is again use the bind method in sequence so we have some input string and then we bind the various
methods that we call in it and since double doesn't return a maybe Mona it simply returns an integer I have to turn it into a function that returns maybe monad so that's why the Lambda function fun is here and then when we have the main function so I have my example inputs 5 minus 3 F so of course only this one should actually end up with a valid value because here the value is not positive so this is going to put us onto the non path and here fu is not an integer at all so
we should already go on the non path here and then because I added support for pattern matching to the maybe monad I can now call this function on each input and then match the result and handle the different cases so in case of maybe N I say that it's invalid input in case the value is an integer I give this as a result and in all the other cases I simply provide default handling so if I run this then this is what we get if we process five result is 10 if we process minus three
we get an invalid input minus three if we process full we also get invalid input so the real world programming here works we don't get an exception because that's transformed into a monat that's actually uh what happens here in par in and we either stay on the valid value path or we go to the non path so this is how that works a final thing you could do to make this a bit nicer is to create a decorator so here I have a function called maybe that contains a wrapper function and then returns that wrapper
as a result this is a standard way in Python of setting up a decorator and then it already contains the part that turns exceptions into the maybe moan out now normally you shouldn't accept for the general exception type you should always handle specific exceptions but because here we're patching into the exception handling mechanism and want to change how it works I think it's allowed to do that so what you see is we have a try so we return maybe and call the function and if there is an exception then we return maybe none so this
does two things it turns a regular function into a monatic function a function that returns a maybe monad and it also transforms any exceptions into maybe n so that's really helpful so if you have here for example the parse int value so returns a maybe int we can now simply do it like this have it return an INT and [Music] then we can throw away all of this code that we had here and use the maybe decorator and now parcent is a monatic function and I did the same thing with the double function so now
if we run this version of the code we're going to get exactly the same result but we really simplified things a lot by having this maybe decorator so we can use our existing function and simply turning into something that returns a maybe Monet all right I feel like we've nded out enough now the question is does this mean that we should now switch over completely to using monat in Python well I'm not so sure I mean monads are typically integrated directly integrated into the syntax of a language you shouldn't have to be building this kind
of boilerplate code um a great example of this is the Rost programming language actually it has a maybe style monat but it's called results so Rost has this built in now python doesn't have a syntax like this for mon we have to build it ourselves although there are libraries like the returns library that offers maybe monad and you can use pattern matching to select which version of the track you're on but especially if you're using a bunch of existing libraries it's a bit of a pain to have to turn everything into a Monet so that
you doing error handling the monatic way so my conclusion is that at this moment in Python it's not really a good idea to do this but I do think it's good to know this type of programming exist especially if you plan to work with languages like rust in the future but I'd like to know what you think would you use a maybe Mo ad in your python code to handle errors or do you prefer to stick with exceptions do you see other ways in which monads could help you write better python code let me know
in the comments so I hope you enjoyed this video and gave you a better understanding of what a Monet is so that if you're at a party and a family member asks you hey do you know what a mon is you say it's a monoid in a category of endofunctors and you actually know what you're saying now there's lots of other aspects of functional program that are interesting especially if you look closer at the Fun tools package in Python and did a video covering the fun tools package specifically so if you like that kind of
thing you should definitely watch this video next thanks for watching and see you soon