Monads are a class of hard drugs
ix.io
ix.io
Monads are about sequencing. They are an interface to force applying functions in a particular order. This is why Haskell needs them -- in a language with only pure functions, there's no way to encode (for example) the order of 'print' statements directly, it has to be expressed in terms of combining functions.
Consider the interface, expressed in a C-like syntax:
interface Monad {
template<A>
from(value: A) => Self<A>;
template<A, B>
sequence(self: Self<A>, f: fn(A) => Self<B>) => Self<B>;
}
You can see that it forms a sort of one-way valve for functions from one value to another.Types you could implement that interface for:
* Lists: `from` wraps the value in a size=1 list, and `sequence` becomes "apply the function to each value then concatenate the results".
* Optional: `from` is the case of a value existing, and `sequence` is "if value exists then apply the function, else return None".
* Sets: same as lists.
But it's hard to write a blog post about an interface with only two functions, especially if you've already committed a thousand words to making it seem complicated.
do
a x
b x
in Haskell and that means the same as a sequential program (a runs before b). But this is not necessarily the case. It does not imply an order. It will only do so if the statements are dependent on each other - do
c <- a x
b cThe effect described by (a x) certainly runs before the effect described by (b x).
do
f <- openFile "test.txt"
putStrLn "Hello World"
contents <- hGetContents handle
In this case the file is most likely not opened before Hello World is printed to the console. However it would be opened before contents is read. This is what I mean in that the ordering is not top-down. It is dependency driven. This really tripped me up at first. import System.IO
main = do
file <- openFile "hello.txt" ReadMode
putStrLn "opened hello.txt"
contents <- hGetContents file
print contents
I ran it with: ghc main.hs
strace -fo strace.log ./main
The strace log confirms the file is opened before putStrLn writes its output: ...
96748 openat(AT_FDCWD, "hello.txt", O_RDONLY|O_NOCTTY|O_NONBLOCK) = 3
...
96748 write(1, "opened hello.txt\n", 17) = 17
96748 read(3, "this is a file\n", 8192) = 15
96748 read(3, "", 8192) = 0
96748 close(3) = 0
...
96748 write(1, "\"this is a file\\n\"\n", 19) = 19
...
Do you have an example that demonstrates IO not happening in the order of the monad? I really don't think what you say can possibly be right, but I'd like to see it if it is.This article discusses it further, down around where it says "Newcomers might think that the order of statements determines the order of execution." and then proceeds to give examples further down.
I have experienced this in the wild. I was working with a lot of data/csv parsing code along with some heavy computer vision work in haskell (3d reconstruction from point clouds using OpenGL and Repa, and custom algos) about a decade ago. It's been a while but I remember this happening quite well.
But in this case the code here is basically generating a list of "instructions" for the Haskell runtime, like saying "first open the file, then say hello, then read the file".
The generation of the instructions may be in an undefined order, but that doesn't matter, because writing the instructions doesn't in itself produce any side effects.
The resulting list of instructions ends up listed in the order you wrote, and so the actual IO effects are executed in that order.
For example with Lists the sequence is not the list itself but the sequence of flatMap[0]/bind/>>= operations necessary to build the desired list starting from `[a]`
[0] https://developer.mozilla.org/en-US/docs/Web/JavaScript/Refe...
> Monads are not about sequencing. They don't force applying functions
> in an order, at least any more than regular function application does.
Monads are about sequencing. This is easier to visualize if you de-sugar them: f = do
x1 <- m 1
x2 <- m 2
return (x1 + x2)
is an imperative form of f = m 1 >>= (\x1 -> m 2 >>= (\x2 -> return (x1 + x2)))
You can see that there is no way to evaluate `x1` without first evaluating `m 1`, even if the result is a thunk. > Haskell does not need them, they're just an interface and don't
> add anything to a language you can't do without them.
How would you recommend implementing do-notation without the Monad typeclass? Applicative functors aren't enough. > The monad instance says IO (IO a) is "the same as" an IO a, so really
> any sequencing ability implied in IO (IO a) already pre-exists in IO a.
I don't see your point. That may be true of IO, but it's not true in general. `Maybe ()` and `Maybe (Maybe ())` are different types.Isn't this true of `f(g(x))` as well? You can't evaluate f(g(x)) without evaluating g(x) first, right?
putStrLn :: TheWorld -> String -> (TheWorld, ())
main :: TheWorld -> (TheWorld, ())
main world = (part3, ()) where
(part1, _) = putStrLn world "Hello"
(part2, _) = putStrLn part1 ", "
(part3, _) = putStrLn part2 "world!"
It would be a frustrating user experience, but it's a valid and equivalent encoding to Haskell's `IO` type.[0] I'm ignoring lazy evaluation for now, but `f` could be implemented such that it returns before `g(x)` is evaluated. This is generally not relevant for purely functional code, but it can affect performance and exception-safety.
the whole point of haskell was to see what happens if try to build a language with the strong requirement of lazy evaluation.
If you remove it you also remove any need* for the IO monad
* you can obviously use an IO like interface in a non-lazy language, but it is not necessary
When I was reading about these stuff, I was not lucky enough to find such a clear high level description of what is the purpose of a monad. I got it eventually, but I wish I encountered this concise description earlier.
So so you're saying bitcoin should have been made out of monads?
I wrote about this extensively in http://www.jerf.org/iri/post/2958, "Functors and Monads For People Who Have Read Too Many 'Tutorials'". If original post is from 2008, then it is the sort of "too many 'tutorials'" that you may have read about them.
The essence of the understanding that Monads act as a wrapper and define lifting and composition of wrapped types is correct in essence and that's the meat of the understanding. Define it a bit more rigorously and it's literally mathematically équivalent to saying "monoid over the category of endofunctors".
You can use it with the list monad for example and not IO at all.
And yes, that goes with the list monad too when using do notation. It's syntactically and in practice almost as if you were mutating a new list and then returning it.
do
r1 <- a 1
r2 <- b r1
r3 <- c r2
r3
This is about composing functions. It has nothing to do with mutability. It is just a convenient way to write the composition rather than using nested bind operators.This is a good description of one particular monad, the ST monad: https://wiki.haskell.org/Monad/ST
The ST monad (enclosing mutable state to use it in a linear, non-branching fashion) is indeed a good example of a monad.
Calling that the essence of monads is as accurate and helpful as calling it a burrito [1].
Future downvoters may want to try to reconcile any of the other common monads (Maybe, Either, List, State, Parser, Reader, Writer) against this enclosed/mutable/linear/non-branching definition and see if the essence fits.
This type of intuition always works on Monads. If you want to learn more about it I'd recommend to learn about the relationship between the Kleisli Triple and the monad. In reality this way of understanding this is just an intuition of how the Kleisli Triple works and how it is an equivalent to the traditional representation of the monad.
"The essence of *the ST monad* is to enclose a mutable state, providing only a set of carefully-crafted combinators for using it in such a way that you can't duplicate the state but have to use it in a linear, non-branching fashion."
I claim that the following 4 are incorrect: "The essence of *the Maybe monad* is to enclose a mutable state, providing only a set of carefully-crafted combinators for using it in such a way that you can't duplicate the state but have to use it in a linear, non-branching fashion."
"The essence of *the List monad* is to enclose a mutable state, providing only a set of carefully-crafted combinators for using it in such a way that you can't duplicate the state but have to use it in a linear, non-branching fashion."
"The essence of *the Either monad* is to enclose a mutable state, providing only a set of carefully-crafted combinators for using it in such a way that you can't duplicate the state but have to use it in a linear, non-branching fashion."
"The essence of *the Parser monad* is to enclose a mutable state, providing only a set of carefully-crafted combinators for using it in such a way that you can't duplicate the state but have to use it in a linear, non-branching fashion."
> In all cases an operation on an object of the monoidal type will mutate the wrapper type but not the wrapped type which always stays the same.I know of no type mutation in Haskell.
> If you want to learn more about it I'd recommend to learn about the relationship between the Kleisli Triple and the monad.
The original author has made an overfitting error, taking a specific learning about ST and incorrectly generalising it to all monads. If I were to relate it to Kleisli triples, wouldn't I be overfitting even worse?
"The essence of *the Kleisli triple* is to use abstract types to enclose a mutable state, providing only a set of carefully-crafted combinators for using it in such a way that you can't duplicate the state but have to use it in a linear, non-branching fashion."All four are correct. That is what they do in essence. What the author is describing the intuition of the Kleisli triple. The only thing that is questionable at all is the mutable part, but that can be excused because later the author goes back on it and clarifies it's merely an emulation of mutability
> I know of no type mutation in Haskell.
"Monad m" is type mutation. You are making the type m into a monadic type "Monad m" that still exposes all of the characteristics of the type m.
> The original author has made an overfitting error, taking a specific learning about ST and incorrectly generalising it to all monads. If I were to relate it to Kleisli triples, wouldn't I be overfitting even worse?
Kleisli Triples and Monads are strictly equivalent. A Kleisli Triple is just a different way of defining a monad. So it can't ever be "worse" than on monads because they are strictly equivalent.
Now as to the quote, the only real problem is "mutable" if you take it too literally. Enclosing a state around the element is what the unit function does, the wrapper itself is what the monadic type provides, and the sequencing of functions in a linear non-branching fashion to work both on the base type and on the state is what the join combinator does. Except for the whole mutability kerfuffle it seems like a fairly straightforward intuitive way to put the Kleisli Triple.
The essence of monads is not about what use can be made of them either, just like the essence of things that have a hook is not that you can use them to hang your coat.
Simply put, monads are data types that share a set of properties. Think of them as you would think of the atoms that are halogens, or of the animals that are mammal. In the case of monads, the property is that functions can be chained on them in a specific way, and that the result of this chaining respects some rules.
The reason it is so hard to explain or understand monads is that this property in many different ways for many different ends, and that the what and the why are often mixed in the explanation.
Isn't it just that you can define monadic interfaces for lists, as you can with (probably all) parametric data types?
Saying "the list monad" wouldn't be correct, right? Although it's pretty common to see that phrase.
You can say "the list monad" reasonably because there is only one sensible implementation of (List a -> (a -> List b) -> List b) in Haskell. You could unconditionally return an empty List b and fulfill that interface, but what would be the point of that? You can't just return a list of all the things the function call resulted in because that would be a List of a List of b, which is not the same as a List of b. You can't sort them in Haskell because you don't have any form of comparability available in that interface specification. etc.
Technically in other languages this may be less true as due to having fewer restrictions (for example, you may in a language that defines ordering on all values, and thus you could sort the result regardless of the types involved) you can do more things, but there's still really only one sensible and unsurprising implementation.
While this may be true, I think the main reason you hear "the list monad" in Haskell is that Haskell doesn't support multiple instances of a type class (such as Monad) for a single data type (such as List/[]); and the Prelude already includes such an instance, which then becomes the List Monad.
This limitation is perhaps not often relevant for Monads, but it is sometimes relevant for other Algebraic structures. For example, there are two very common Monoids on Number: (Number, +, 0) and (Number, *, 1).
Technically, if you were the pervert kind of developer, you could revert the list.
It would therefore be correct (but slightly pedantic) to talk about "the list monad".
> as you can with (probably all) parametric data types?
Actually, you can't. For instance, if you define a predicate type:
`data Predicate a = Predicate (a -> Bool)`
It is not a monad, it's not even a functor. (If you're interested in what it is you can look into contravariant functors).
The understanding of the monad as a wrapper with a combinator equivalent to function composition is simply correct.
A list is absolutely a monad.
You actually can't implement Monad for most contravariant functors.
Monad is an interface, just like List. You don't implement a list using a monad, anymore than you implement a list using a set or a tree.
You aren't allowed to touch the contents of the hot cell, instead you must send a robot arm into the hot cell. What is surprising though, is that the robot constructs another hot cell inside the hot cell and then merges it with the outer hot cell
The problem with monad is that it is a typeclass with not much meat on it, it would be more appropriate to talk about monad instances.
>it's simple, monads are like X
>you are wrong, it's actually simple, monads are like Y
>you are wrong, it's actually simple, monads are like Z
&c
- Read-only values (e.g. environment variable, configuration)
- Write-only functions (e.g. logging, compiler output)
- State (read-write-values)
- Sum-types (e.g. Optionals/Maybe, Either/result)
- Inductive lists
- Arrays/vectors
- Contexts (e.g IO, file handlers, database cursors, etc)
- Functions
- Sequenced operations
- Continuations
- Parallelization
- Concurrency
- Interpreters
- Compilers
- Combinations of all of the above (plus more)
- And whole programs
- (and a whole lot more)
All of these can be built as monads, since it’s an extremely general abstraction. But when you try to attach the specifics of any of these constructs to your explanation/view/understanding of monads you will inevitably run into issues/contradictions with other things that are also monads.
At its core, monads are just an interface with two (or three) functions:
- unit/pure/return, which puts a value into a monad
- bind (or map and join) which combines a value in a monad with a function from that value into the same monad
(Plus some algebraic laws about how these functions behave when combined.)
However, the technical simplicity of this interface does not make it easy to understand how and why it’s a useful abstraction over so many programming constructs.
IMHO, the best way to get a grasp of this, is to actually use and play around with a bunch of of these constructs via these monadic functions.
(I am also thoroughly convinced that you can ignore Category Theory if you are only interested in monads as they are used in programming.)
Maybe I should try again not that I'm a bit older and more experienced...
Consider the following code where you would like to multiply a number by two, but this number is potentially undefined:
var x = ... // a number or null
if (x == null) return null
else return x * 2
Now, imagine if x were a list containing zero or one numbers (if the number is undefined, the list is empty). In this case, you could refactor the code as follows: var xs = ... // a list of zero or one numbers
return xs.map(x => x * 2)
The advantage of the second snippet is that there are no branches, thus making it easier to reason about the code.Thus, in order to understand 80% of their utility in 20% time, I can recommend that you think of a monad as a wrapper around a value which allows you to treat special cases uniformly. In this case, a list of one or zero items. Of course there is more to it, but I this is usually enough to get people to stop worrying and interested in exploring more.
From there, it comes with practice and you can see how bind/return work for other things (notably concurrency). There's no need for any theory to use monads effectively.
To me, the problem with Haskell is that you need monad from the beginning, whereas in OCaml, you can start using them when you're familiar with basic ML.
IME the biggest hurdle is convincing yourself you've actually understood them, because they seem so mundane that it's weird they even have a name.
kinda.
they are needed where pure functional and lazy evaluation converge
in a pure, lazy language, you compute things based on dataflow
but what is the dataflow of a keystroke from the keyboard? you need an abstraction to get stuff like this into a pure, lazy programming environment
that's all monads are...abstractions to solve a problem
https://adit.io/posts/2013-04-17-functors,_applicatives,_and...
For this same reason, my favorite explanation of monads is actually https://journal.stuffwithstuff.com/2015/02/01/what-color-is-...
Why? Because it does not explain monads at all. In fact, it does not even have the word "monad" in it. But the whole mess about "function coloring" brought up to describe async code is basically the same thing as the "mixing crack and heroin" problem in the original post here - at least in terms of developer experience. JavaScript has two built-in, uncustomizable monads: imperative and async; with the async monad being a superset of imperative but being harder to call and manage.
Rust has at least four: normal imperative code, Option, Result, and Future. We can't really talk about Monad<T> as a type in Rust because its trait system lacks higher-kindedness; so no monad transformers or colorless functions. But they all have combinators that let you sequence functions and store state away in the type. Option/Result let us hack exceptions into a language that does not have exceptions[0], and Future lets us hack user-mode/green threads into a language that is committed to OS stacks.
All the monad tutorials fail because they try to walk you through the underlying math that makes monads work in a functional language. This is wrong; nobody needs to understand the category theory behind the curtain even if it seems super-cool. It's just a hack to force ordering and state into a language that does not have it.
[0] panic!() does not count as an exception mechanism, even though it can be implemented with stack unwinding. There are panics that don't unwind (stack overflow) and you can compile programs that terminate-on-panic instead of unwinding.
Monads are from category theory, a branch of math.
Category theory is the study of generic, simple patterns that work in many different contexts. The insights from recognising those patterns and how they fit together helps to find common ground between different areas of math that initially seem distinct. It's like a kind of OOP and pattern language, but for math instead of programming.
Some patterns got names like Monad and Monoid because they were seen to crop op often, and useful insights could be gleaned from recognising the same pattern in different contexts.
Then someone doing functional programming (FP) realised that the Monad pattern from category theory could be used as a good generic structure in FP to encapsulate many different patterns that were already being used in FP another way.
Things like IO, state, efficient arrays, controlled parallelism, even kernel-like scheduling and user interaction, were already being done in pure FP before the introduction of monads. And before Haskell, which itself is quite old (~32 years). So FP didn't need monads. You just threaded all your functions together "by hand", using things like continuation-passing style or accumulators. There are several ways to do it, and it was possible to write large and sophisticated program this way.
One day, someone realised that much of this threading things together like IO, state, etc could be done in a generic pattern called Monad with a signature (like a generic or interface in OOP) equivalent to the rules of Monad in category theory from math. They also developed the idea of monad comprehension syntax, like a list comprehension but generalised to any monad.
Haskell was fairly new then, but it already worked fine without monads. These new-fangled FP monads and associated syntax so excited people in the Haskell community that they added them to the language, and redid the standard library around the concept. (Like the way Python initially didn't have list comprehensions, or async, but after those were added to the core language people started to use them a lot.)
Nowadays, monads are seen as a functional programming thing, but really there are many FP languages that don't use monads, and monads aren't just for FP. They aren't necessary, but they are a nice pattern that helps write clearer, longer programs that carry things like state and side effects in a pure-functional world.
The reason there are so many different kinds of Monad in Haskell, not just IO, comes from the same reason they were identified in category theory: They name a basic pattern that occurs naturally in many different contexts where it's not initially obvious. Once named, you can combine them. Unfortunately this leads to some confusing tutorials in Haskell, because Monad is a generic (in the OOP sense) abstraction for many kinds of "threading things together", which covers a lot of things you may write that seem very different from each other.
To give you an analogy, tensors are maybe more common in Python than in other languages because of the datascience happening in that community, but that doesn't make tensors a Python thing, and Django folks probably don't care about it.
An example of monads being used in other languages would be all the stream libraries having a flatmap operation somewhere. They don't call it "monad" so they can't brag about it on Twitter, but they're still using it.
I am curious how much of some of the side comments made in the paper are still relevant? The quips about slowness, etc. Are these things that have dramatically shifted in 14 years, or only incrementally improved?
As a pharmacologist, the phrase is meaningless to me. There is literally no distinction between 'hard' or 'soft' drugs
The terms are meaningless to me, I have no reasonable way of classifying anything into those categories because the categories literally have no meaning
Glad I'm not a pharmacologist, so I can understand the term in this context.
The terms and how they are used have zero correlation with reality
"an addicting drug capable of producing severe physical or psychological dependence, as heroin."
But you already knew what was intended.
Caffeine is a hard drug, it also produces physical and psychological withdrawal symptoms
Nicotine is a hard drug, it produces physical and psychological withdrawal symptoms
Apparently the term includes all psychoactive drugs, which makes the term meaningless
Searching for 'hard drug' on the National Institute on Drug Abuse yields no results that contain any information on 'hard' or 'soft' drugs. Don't you think the national agency would have a definition if it were a real concept?
https://nida.nih.gov/search/Hard%20drug?sort=_score
Searching for 'soft drug' just yields no results at all
https://nida.nih.gov/search/Soft%20drug?sort=_score
Here is an article by a psych professor who works in the field of addiction:
https://www.verywellmind.com/the-difference-between-soft-dru...
If your technical definitions don't include this taxonomy, so what? It's a meaningful term among laypeople because it communicates exactly this taxonomy in a single word.
It doesn't matter if some people can agree on something when that concept is objectively and demonstrably not only false but systemically harmful
PS alcohol is more harmful than heroin physiologically. Alcohol would be one of the 'hardest' if we were going by physiological and societal risk
Editing to add: we can't not include alcohol and nicotine in the general category of drugs. They are undeniably drugs and anyone who tells you otherwise is a victim of the previously mentioned misinformation
If the author had written "Monads are a flavour of Pringles" (once you pop, you can't stop), it would have been marginally less bad an analogy.
There are also some incorrect definitions being peddled by law enforcement like higher chance of addiction, however hard drugs like LSD clearly contradict that.
As far as this article goes, I read it and I'm not sure what part of the hard drug meaning was meant to be communicated here.