I didn't understand it for years then realized I had seen it all over the place.
Now that I think I know what it is I can explain to someone that doesn't already know what it is.
I didn't understand it for years then realized I had seen it all over the place.
Now that I think I know what it is I can explain to someone that doesn't already know what it is.
Here it goes:
1. One understood monads when one is able to write a monadic interface that behaves like any intermediate haskell would expect it to behave. This is not a hard task.
2. Screw the abstract stuff. While fascinating, really few people understand concepts going from abstract to concrete rather than the other way around. And don't get me wrong, I don't think these kind of people are better theorists or problem solvers, they just have to seem a special relationship with symbols. If you are one of those people, you would likely know - in any case, I would encourage to write 3-5 examples of monads in Haskell or Java or JavaScript or whatever language suits you and go from there.
Programmers without a mathematical background will naturally struggle with such concepts. You can't point to where in the the code "the monad" is, as it's just the pattern, or the abstract mathematical combination of the type and the operations. Sure you could pack them into a class or a module, but it's still hard to point to where "the monad" is.
When we describe other patterns, like a Singleton, Factory, an Adapter, etc., it's is much more clear what and where the thing is. It's just a word to describe a function, a class, or a module.
Perhaps 'function composition with metainformation' would be a better title for the pattern. You take composable functions that act on certain types, and extend them to support metainformation attached to the values, while defining how the metainformation combines.
Even to just be able to rapidly identify why having a thing that .map()s is a meaningful object represents the sort of experience with and abstraction of these concepts that'll put you ahead of the game for understanding monads.
Monad should be `FlatMappable`. I found this great article where they use exactly these terms to explain it:
https://dev.to/joelnet/functional-javascript---functors-mona...
If people are using different terms to explain your confusing terminology then... maybe the confusing terminology is not very good.
(The mathematical basis helping you get deeper insights into it is just a bonus, albeit IMO a valuable one.)
On the other hand, if I say "it's a monad" then people who have encountered the term before know what I mean (even if they've had no need to understand monads in depth) and people who have not will successfully find information about the right thing if they search for it.
Monads have a particularly bad reputation for being hard to learn, despite appearing in non-general form in every modern language. Since people seem to have little trouble grasping the way in which futures, streams, and options work I don't think there's really such a great challenge involved - but the culture of running screaming from the mere word doesn't help matters in the least.
For what it's worth, "functor" properly describes the combination of a type and its relevant .map() function. It's (common) shorthand to refer to just the type as "a functor" when there's a unique, well-known functor that utilizes that type.
So, we could be more precise, of course. But also if you just throw up your hands when someone says "a list is a functor" then you're fighting a difficult battle.
A monad could be described as "sequencable/chainable", but that metaphor is quickly breaking down as there as so many ways in which monads show up.
These days you should probably add Foldable and Traversable to that list, as they are closely related and appear throughout the standard libraries and even in the Prelude.
Traversable is a generalization of Functor to include Applicative effects. It reduces to Functor if the Applicative happens to be the Identity type.
Where Functor and Traversable replace values one-to-one while preserving structure, Foldable represents reducing operations (folds) which consume the values from a structure in some defined order to build up a result. This includes basic functions like sums and products, as well as more complex operations like `sequence`.
Hmm well it might break down but simply by choosing a better word you've helped me understand what a monad is more than any article I've read on the matter.
A monad is an applicative functor which is a functor. Each of these concepts in the hierarchy comes with a set of laws which must be obeyed. For example, all functors (which define an operation, fmap) must obey 2 laws:
Identity
fmap id == id
Composition
fmap (f . g) == fmap f . fmap g
All instances of Functor must obey these laws. Furthermore, since every Monad is also a Functor, all instances of Monad also obey these laws (in addition to extra laws).
The main difference is that design patterns have 'fuzzy' rules whereas monads are more precisely defined.
After trying to understand it at a more fundamental level I have a suspicion that that piggy wiggies are involved.https://www.google.com/amp/s/bartoszmilewski.com/2014/10/28/...
In short: Functors (a tightly related concept) are about types that have ‘.map()’, and Monads are Functors that in addition have ‘.flatMap()’.
Effects are encoded using a polymorphic type with a `map` operation (called a functor), and effectful computations are functions `a -> E b` for some types `a, b` and effect `E`.
A monad is exactly the plumbing you need for composing effectful computations: it turns a computation `b -> E c` into a `E b -> E c` so that you can compose `a -> E b` with `E b -> E c` to get a composed computation `a -> E c`.
It does so in a way that makes effectful composition associative (i.e. the way you'd expect)
Yes, Maybe/Options are a way of encoding partial functions/exceptions.
List is a way of encoding non-determinism.
There's a name for the monad in which there is no effect: the identity monad.
The heart of a monad is the bind/flatMap/and_then interface, in which the value contained in the monad is computed on to produce a new monadic value. This is where the effect occurs: something other than just running the function happens.
If the bind implementation only runs the provided function then there's no effect, and it's the identity monad.
The problem with many monad explanations is they explain only particular monads. IO and State can be thought of as effectful computations, but List operations cannot.
Monads are just a pattern for composing computations, it doesn't say anything about the semantics of those computations.
They can, the effect modeled by the list monad is non-determinism.
Monads are an approach to modeling the idea of running a set of statements in a particular environment, with specific input and/or output capabilities, often called "effects".
In your typical programming language, you've only got one type of monad, which is the language runtime. You can do literally any type of effect at any time. That is very powerful, but it also makes programs difficult to reason about.
In many typed functional programming languages, monads are used to create much more specific contexts for running sequences of computations and effects. That makes reasoning about local sections of your program way easier.
One might think "why would I want to limit my power?" And the answer is you're only limiting yourself locally. It's exactly the same as how procedural programming dispensed with the all powerful GOTO, but it turned out that breaking programs into functions made them much easier to reason about.
And it turns out that once you start thinking that way, you can often offload some pretty tricky logic into a well designed monad.
A major caveat is that things can get pretty complex when you try to compose monads, and for me, that's where the love affair ended. There are alternative models for how to constrain effects, like algebraic effect systems, which try to be more intuitive and composable.
The problem with many monad explanations is they only explain particular monads but then other monads become even more confusing.
So it's a little different from thinking of your program as something that is once. Instead, you have your program being run multiple times, with the environment injecting different at each stage.
I think the Future/Promise monad is similar. The environment is taking responsibility for pausing and resuming computation.
But I'm curious if you have other monads in mind where the analogy fails.
And I think it holds just fine to think of the monad as injecting data into a sequential computation before each step and ingesting output after each step (boxed in the monadic container).
Which reminds me, that boxing is also part of the power of monads. It means you can nest steps of a monadic computation, instead of just chaining them, as a functor allows. This stimulates lexical scope -- bindings that exist for all statements after the one that defines them.