4 ms·
I think Monad make more sense when you understand the context and its additional constraints as to why you'd want to use Monads. Imagine that you want to write
by didibus 5y ago
I think Monad make more sense when you understand the context and its additional constraints as to why you'd want to use Monads.
Imagine that you want to write code that is strictly pure, so that when evaluated, it doesn't do any side effect, it only returns a pure description of the computation it did.
Here's a simple example:
func announce-good-catch(fish)
if(fish.size > 5)
print "We found a big one!
else
print "That one won't even feed a cat, try again!"
How can you make this function pure? It technically prints something, yet you want to run it and not have it print, but instead return you something that describes what branch it took and what effect it would have done had it been impure.
You can do this:
func announce-good-catch(fish)
if(fish.size > 5)
return [:print "We found a big one!]
else
return [:print "That one won't even feed a cat, try again!"]
Now the function is pure, it doesn't print anything, it evaluates the branch to take based on input and return to you a description of what it should do.
The problem is programming that way is hard, let's add another function:
func get-fish(fish-id)
return new Fish(db.query("select * from fish where id = " + fish-id)
Again this isn't pure, so let's make it pure:
func get-fish(fish-id)
return [:new Fish [:query [:get :db] ["select * from fish where id = " + fish-id]]])
Already this is getting pretty complicated, getting the DB is impure so return a description of it, querying the DB is impure, so return a description of it, Fish can't be created if we don't execute the side-effect, so take note that you want to create a Fish after those two side-effects were to actually happen.
Now combine get-fish and announce-good-catch(fish)?
announce-good-catch((get-fish 10))
Well this works in the impure version, but this doesn't work for our pure versions because now the input to announce-good-catch isn't an actual Fish but instead it's this:
[:new Fish
[:query
[:get :db]
["select * from fish where id = 10"]]]
And so it's not going to work, you can't compose those pure functions as you'd normally would.
Here is where Monads become really handy, they put all this behind an abstraction that lets you with added syntax sugar feel like your code is written in the impure style, yet under the hood it will convert things to a description of all side-effects and whatever pure behavior is pending the result of those. And it'll let you compose those descriptions together so that from the programmer point of view the code you write looks and feels almost the same as the impure version.
This is the problem Monad solves.
Now once you have that description, eventually you want it to actually run for real so that side-effects and all other described pending behavior gets executed. But what people realized here is you can kind of abuse this a bit, where you could choose to interpret the description in new ways, or manipulate the description prior to executing it. Basically whatever executes the description for real is in charge of how to do that and it could choose to change some of the assumptions like the evaluation order or the thread on which thing execute, etc.
Thus once you have Monads, you can also use them to manipulate evaluation order and semantics.
Now if you don't care about constraining yourself to pure functions, all this can seem like overkill, and it kind of is.
Obviously, people will tell you it's a good idea to make everything pure, because you can more easily test things.
And people will tell you, even if you don't care about the purity, how Monads let you manipulate evaluation order and semantics of the code can be cool and allow for some neat features, so maybe that's something you'd find useful, such as adding tracing throughout automatically, or automatically parallelizing things that don't depend on each other, etc.
This is where some people say that Macros can often be used to similar effect. Because macros, a bit like Monads, operate on the description, macros on the code's AST itself, Monads on the captured Monadic composition. So a macro can also rewrite things to change their evaluation order or semantics.
I wouldn't call myself an expert on all this yet, more of an enthusiast, but that's how I would contextualize it.
- Koshkin 5y agoI think the List monad is easier to comprehend.
- didibus 5y agoThe List Monad is the same, if a function called with the same input can return one of many results, each time it is called it could return a different output. That means this function isn't pure, because a pure function maps the same input to the same output even when called repeatedly. The difference here is instead of returning a description of the impure things that should be done, you can make the function pure by returning the list of all possible options. That means instead of getting a different output non-deterministically for the same input each time the function is called, you now get the same output everytime, which is the list of all possible outputs for that input. So the context is the same, you want to constrain yourself to pure functions for some reason, so how do you make this pure? func favorite-color(person-name) if(person-name.startsWith("A") return randOf("blue", "green") else return randOf("yellow", "black") The trick is to have randOf return a list of all options such as randOf("blue", "green") => ["blue", "green"] Now you have the same composition problem as before: func generate-favorite-color-string(favorite-color) return "My favorite color is: " + favorite-color("Abel") generate-favorite-color-string(favorite-color("Abel")) But this won't work again, so you need a Monad once more to help you with composing these pure versions of your impure functions so that: bind(favorite-color("Abel"), generate-favorite-color-string) Which says: compose generate-favorite-color-string with favorite-color using Monads And now what the Monad composition will do is it will automatically run generate-favorite-color-string for each element in the list returned by favorite-color and itself will return a list of all those possible results: ["My favorite color is: blue", "My favorite color is: green"] So the composition is also pure now.
- lmm 5y agoThe list monad is simple but not a representative example of monads in general. Teaching it to beginners tends to create major misconceptions like thinking all monads are collections or collection-like or "have values in".