From Monads to Machine Code
stephendiehl.com
stephendiehl.com
I know mathematicians might love it, but forgetting a symbol can mean I don't understand an entire block of code. Whereas it could have been made so much clearer with one word.
However, I do think the built-in symbolic syntax (::, ->, =>, =, |) is all pretty clear. And thankfully there’s Hoogle to find the meaning of any operator you don’t understand, or even to find a function that you think should exist of a certain type. So I dunno, we get by well enough. And you could actually write your code largely without operators if you wanted to, either because there’s a standard name already, or because it’s easy to define an alias.
For example, I have no clue what >>= and <$> mean from a glance.
I have to look it up to get Monad and Functor. Why not use the word?
The Haskell Wikibook is an underrated resource: https://en.wikibooks.org/wiki/Haskell/Understanding_monads
Why does it exist at all? Speaking loosely, monads are about composing functions that look like a -> m b. Here's regular composition alongside the 'monadic' one:
(.) :: (b -> c) -> (a -> b) -> (a -> c)
(<=<) :: Monad m => (b -> m c) -> (a -> m b) -> (a -> m c)
As you can see, the same except for the m wrapping each function's results. Just like normal function composition, there is an identity element - that's what return is. Again, a comparison: id :: a -> a
return :: Monad m => a -> m a
Again, the same except for the m wrapping the result. Both composition operators are associative, and both have the same relationship with their identity operation: id . f = f return <=< f = f
f . id = f f <=< return = f
(That's why there are monad laws, to ensure those properties hold.)So return is very straightforward: it is the identity of functions that return monads. It's unfortunate that both the name and the presentation of monads in Haskell tend to obscure this.
I appreciate the effort though.
The return type is inferred, and the type is what the type class machinery is driven by. You can get some more visibility into that by playing in ghci:
return 0 :: [Int] => [0]
return 0 :: Maybe Int => Just 0
Having behaviour driven by return type takes a bit of getting used to.e.g.
return 'c' :: Monad m => m Char
You can actually sort of think of this as a function from typeclass dictionary to value, except the compiler will pass that dictionary itself once it's inferred what it should be (and will then inline/optimize it away, where it can).
The 4 things you do with an interface are:
1. Declare an interface
2. Describe the contract of the interface
3. Declare that a datatype (or class) adheres to that interface
4. Provide an implementation of the the contract of the interface (fulfill the contract).
A haskell Typeclass does exactly that. The keyword "class" is where you do #s 1 & 2. The keyword "instance" is where you do #'s 3 & 4.The only notable difference is: If an ISizable has int GetSize() method in haskell instead of the contract being int ISizable.GetSize(), it is static int GetSize(ISizable x).
Or in Haskell:
instance ISizable MyType where
GetSize :: ISizable -> Int -- Note you don't actually type this line
GetSize x = ...
The Maybe monad stops when it encounters a Nothing result, and continues when it encounters a Just. So:
Just 1 >> Nothing >> Just 5
will return Nothing, whereas Just 1 >> Just 2 >> Just 5
will return Just 5.>>= is used to take the value inside the monad (Just) for the left-hand argument, and stuff it into the right-hand argument, like so:
Just "hello" >>= Just . (++ " world")
returns Just "hello world".Is that all that is needed for something to be a monad?
But the intuitive version is that anything with those two operations that doesn't smell funny is a Monad.
(Think of it like implementing an interface: anything that matches the type signature will work, but if you write something silly, it won't work right)
I know it sounds ridiculous but a) it took me about seven evenings b) it's a lot of fun and c) I guarantee you'll get some proper programming insight, whatever language you use for a day job.
pflags = protRead <> protWrite
mflags = mapAnon .|. mapPrivate
What's the difference between these two operators? instance Monoid ProtOption where
mempty = protNone
mappend (ProtOption a) (ProtOption b) = ProtOption (a .|. b)
[1]: https://github.com/sdiehl/tinyjit/blob/master/src/JIT.hs