Monads without pretension (2011)
rcrowley.org
rcrowley.org
The best introduction to monads was written in 2006 (more than eight years ago now) and I don't imagine that a better one will be written in the near future -
http://blog.sigfpe.com/2006/08/you-could-have-invented-monad...
The example:
return 7 >>= (\x -> Writer (x+1,"inc."))
>>= (\x -> Writer (2*x,"double."))
>>= (\x -> Writer (x-1,"dec."))
works for this example, but a more accurate representation of how "do" notation is expanded is: return 7 >>= (\x -> Writer (x+1,"inc.")
>>= (\x -> Writer (2*x,"double.")
>>= (\x -> Writer (x-1,"dec."))))
Note that I've moved the parentheses.Actually, you can, equivalently, omit the parentheses for the same meaning, because the '->' has very low precedence:
return 7 >>= \x -> Writer (x+1,"inc.")
>>= \x -> Writer (2*x,"double.")
>>= \x -> Writer (x-1,"dec.")
The point is, most times each of subsequent functions need to "see" what was in scope in the earlier functions. Most times, the later monadic actions need information that is pulled out of the monad in previous actions.For example, consider the following:
main = putStrLn "Enter your first name" >>=
\_ -> getLine >>=
\first -> putStrLn "Enter your last name" >>=
\_ -> getLine >>=
\last -> putStrLn ("Hello " ++ first ++ " " ++ last ++ ".")
The last monadic action needs both "first" and "last" to both be in scope. This only works because of the way that it is implicitly parenthesized.Until I "got" this, I was very confused by most of the Monad examples I found.
Here's an article I wrote about the IO monad which also makes this point: https://github.com/Patient0/IOMonad
It is perfectly reasonable to also read this as the list of elements of monadery that even tutorials written in Haskell tend to miss or fail to explain.
> Monads are not "about effects".
What is your definition of "effects"? I ask because this article[0] and many others[1] seem[2] to use a contradictory definition, in which failure, nondeterminism, state etc. are viewed as effects and monads are a means of implementing those effects.
------
[0]: http://www.sciencedirect.com/science/article/pii/S2352220814...
[1]: http://scholar.google.com/scholar?q=algebraic+effects+monad&...
[2]: as far as I understand them; I may be wrong.
So to summarize: effects are a general concept, monads are a particular technology for implementing that concept.
Further complexity ensues when people start talking about the general concept of a monad which is interesting in its own right but it has a more sophisticated relationship with the concept of effects.
[0] Purity is a property of, say, functions. Its definition is a function `f` is pure if and only if
const () . f = const ()
which usually means that non-termination is impure as well. The notion of equality you use above can finesse this definition a lot.[1] As stated in [0], non-termination is an effect, so Haskell monads are impure in that sense. Haskell typically ignores non-termination effects, though. Generally, monads would work more or less just fine even without non-termination though. Externally you can think of them as pure.
In addition to tel's discussion, what I was really trying to get at is that monads aren't "about" IO effects in particular, they're not about "impurity". In this case the whole thing is pure.
Defining effects at a deep programming language research level can bring a different understanding, where all monads are about effects, but "effects" has a different meaning that most people understand.
I'll ponder how to clarify that better.
... but I may be wrong about that.
The simplest way to encapsulate data is to wrap it using a constructor which we keep private to our module:
module M1 (Encap(), val1, val2)
data Encap a = mkEncap a
val1 :: Encap Int
val1 = mkEncap 5
val2 :: Encap Int
val2 = mkEncap 10
Other modules importing M1 get access to the Encap type, val1 and val2 but not the mkEncap constructor. They can use val1 and val2 as-is, but they can't construct new Encap values or destruct existing Encap values to get at their contents. The problem is, there's not much we can do with this interface.One way we can make this more useful is being able to apply some function to an encapsulated value. That's what Functor is for, so we can add this to M1:
instance Functor Encap where
fmap f (mkEncap x) = mkEncap (f x)
Now users of M1 can transform encapsulated values without being able to break the encapsulation. For example: val1Plus7 :: Encap Int
val1Plus7 = fmap (+7) val1
val2Str :: Encap String
val2Str = fmap show val2
We're still pretty limited though, since there's no way to combine encapsulated values into new encapsulated values, or to encapsulate our own values. That's what Applicative provides, by letting us construct encapsulated values without gaining the ability to destruct them, and by allowing encapsulated functions to be applied to encapsulated values (since functions are closures, this lets us gather up encapsulated values and combine them arbitrarily): instance Applicative Encap where
pure x = mkEncap x
(mkEncap f) <*> (mkEncap x) = mkEncap (f x)
Now users of M1 can encapsulate and combine values, like this: -- val1 + val2
val1PlusVal2 :: Encap Int
val1PlusVal2 = fmap (+) val1 <*> val2
-- New encapsulated string
val3 :: Encap String
val3 = pure "Hello world"
-- val3 repeated val1 times
val3Repeated :: Encap String
val3Repeated = fmap rep val1 <*> val3
where rep n _ | n <= 0 = ""
rep n s = s ++ rep (n-1) s
This is quite a powerful interface, but one thing we can't do is `collapse` double-encapsulated values into single-encapsulated values. That's what Monad provides: join :: Encap (Encap a) -> Encap a
join (mkEncap (mkEncap x)) = mkEncap x
An alternative, but equivalent, definition is to allow calls to encapsulation-producing functions without encapsulating their result. Haskell's Monad is defined this way: instance Monad Encap where
(>>=) :: Encap a -> (a -> Encap b) -> Encap b
(mkEncap x) >>= f = f x
These kind of encapsulated values turns out to hold effects without breaking the language, and these interfaces turn out to be powerful enough for general computation.Thanks Chris!
I wonder if you're hand-waving a bit here. To me "encapsulated values" describes Identity or Maybe, but really doesn't work (IMO) for e.g. State or IO where bind is composition.
Maybe my terminology could have been better, but I meant "encapsulated" in analogy to OOP, which advocates "encapsulating" all data via methods. The OOP definition of encapsulation includes using getters/setters, which is like having "wrapped-up" properties, but the idea is that we can go beyond this to calculate the data in arbitrary ways without our clients having to know about the implementation. That's what I was trying to get at here; for example, the implementation of IO involves horrible imperative yukiness, but we (the client) don't need to know that: we just use the interface, and if our functions ever get called, they will be given an appropriate argument.
http://ericlippert.com/2013/02/21/monads-part-one/
A lot of Monad articles like to explain 'what' without first establishing 'why' (IMHO)
class Maybe {
constructor(value) {
if (value != null) {
return new Just(value)
}
return new Nothing()
}
bind(f) {
if (this instanceof Just) {
return f(this.value)
}
return new Nothing()
}
}
class Just extends Maybe {
constructor(value) {
this.value = value
}
toString() {
return `<Just ${this.value}>`
}
}
class Nothing extends Maybe {
constructor() {
this.value = null
}
toString() {
return '<Nothing>'
}
}
var result = Maybe(2).bind(x => {
return Maybe(3).bind(y => {
return Maybe(x + y)
})
})
console.log(result.toString()) // <Just 5>By contrast, both Applicative and Category as very, very useful.
return '<M a: %s>' % self.a
Should be: return '<M a: {}>'.format(self.a)
And: class M(object):
Should be: class M:
etc.Why do you prefer the explicit method call over an operator?
I can't think of any way that taking the modulo of two strings logically results in interpolation. If anything, it should do something like return the original string with all instances of the argument removed. It's just simpler to not support it at all and use the standard .format() method.
There's also the fact that it's deprecated, of course. :)
class M(object):
is a new-style class in Python 2.x, in which it's the correct syntax: https://docs.python.org/2/glossary.html#term-new-style-classIt's different in Python 3 but that doesn't matter because Python 3 is largely irrelevant.
Seriously, what is this constant obsession with monads that has infected the programmers?
Why not be obsessed with, say, functors? I can then make statements like, "A functor is a wrapper. Anyone who tells you otherwise is being obtuse" and be just as correct, and sound just as smart.
wanders off grumbling to himself...
Monads are mostly about enabling composition between operations returning different types.
That's roughly a description of the IO monad, which is a specific monad. The IO monad is really a degenerate case, and most monads ( List, Cont, Maybe, Writer, State, ... ) have nothing to do with effects.
Half the problem of talking about monads is that in their full generality they're really just a description of a trivial algebraic structure that doesn't really impart much intuition. Monads just get a lot of press because they're one of the simplest examples of a structure that can't be compressed easily in terms of common experience. There's nothing I can point to in our our everyday experience and say "monad" is like this.
This is what monad is (read: interface with two functions):
class Monad m where
(>>=) :: m a → (a → m b) → m b
return :: a → m a
Read: function >>= of arguments M[a] and (fun from a to M[b]) that returns M[b]. You can probably write it somehow in MyPy annotations.(It's actually different in haskell, doesn't matter)
The author however just implemented one instance of Monad: Identity (the most trivial). And called it a Monad. And then semi-checked laws on one value.
In mathematics monads usually arise as adjunctions between two functors, for example beginning with a set of elements, you can consider the free monoid generated by it and forget the group structure, this gives you a much larger set. If you did this operation on a set of characters, you would get the set of all strings of those characters, eta would in this case be the operation that given a character in the character set gives you the corresponding string of length one and mu would concatenate two strings.
Imagine if you wanted to learn about monads, but every article went "Monads are a simple and powerful idea that, interestingly enough, can be very, very well expressed in Latin. Therefore, I will switch to Latin for the remainder of this article. Oh... You haven't studied Latin? You really should! It's really very useful. Moving on... Cogitus sin extricatus..."
If you want your audience to understand, you need to explain it in C.
There is a translation from any typed language into an untyped language. Writing code in that untyped language is not going to be type safe, while the code generated (correctly) in that untyped language from the typed language is still guaranteed to be correct.
It is entirely possible that the only way to get anything safe out of some Haskell code is to rely on checks the Haskell compiler gives you at compile time, which the C compiler cannot give you.
That said, people often underestimate the kinds of guarantees you can bang out of a C compiler, at the cost of a bit of verbosity.
Following on this thought: Every compiler is essentially an assembler programmer! And we all know how error prone it is to code in assembler. So how can the compiler ever produce error-free binaries?
The advantage of mathematics is that it cleanly separates the external view and internal view of a concept. The axioms are easy to state abstractly and they are the most important part, haskell only allows to abstractly define the type of the operations, but can't abstractly enforce the laws. Rather any instance of the typeclass is assumed to satisfy the laws.
Those happen to also hold for certain constructions in functional programming languages, like lists (the list monad) and several others, not by coincidence, but because those languages have a close connection cartesian closed categories.
I firmly believe it is not helpful to explain something by analogy, because an analogy only goes so far. The mathematical notion of a monad is not complicated at all and is only obscured by writing page after page about them in the syntax of some arbitrary programming language.
Given a type `m` and a type `a` a monad provides functions with types:
bind : m a → (a → m b) → m b
wrap : a → m aA monad is some parametric type, T, along with two functions called (in Haskell anyway) "return" and "bind". Return "injects" values into the type taking values of type A to values of type T(A). Bind transforms values of type T(A) into values of type T(B) using a function like A -> T(B).
Then these two functions must follow a few rules.
That's a monad. The Haskell fragment above describes the signatures of those functions, notes how they relate to the parametric type, and also produces a facility for overloading `return` and `(>>=)` ("bind") and even working with them when lacking a concrete choice of type `T`.
All of those questions change their answer depending, terrifyingly, on how you informally define the word "monad".
http://www.kylheku.com/cgit/lisp-snippets/tree/monads.lisp
Monads are developed starting with CLOS, then wrapping macros around them for doing monadic comprehensions. Finally, a monad-defining macro is introduced which generates the class and methods from a succinct syntax, and is used to write several monads, including a state transformer monad.