Last active
August 29, 2015 14:09
-
-
Save kisom/04a08216d821f1cbeb23 to your computer and use it in GitHub Desktop.
Source file for the monad post.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
(This post is a Literate Haskell file that was run through pandoc to | |
produce markdown). | |
It seems customary in the Haskell space to write an introduction to | |
monads; my attempt in this exposition is not to outdo anyone else, but | |
to make the topic clear enough that I can write about it. My hope is | |
that by doing so, it becomes more clear to me. | |
> module Perhaps where | |
At this point in my Haskell education, I am halfway through **Learn | |
You A Haskell** (to the point of having covered functors, but not yet | |
applicative functors or monads), and a quarter of the way through | |
**Real World Haskell**. For me to learn a language requires solving | |
problems, and along the way I've encountered monads. | |
Yesterday, I watched Brian Beckman's excellent "[Don't Fear the | |
Monad](http://www.youtube.com/watch?v=ZhuHCtR3xq8)" talk, and realised | |
I've been using a monad quite regularly in my experiments thus far: | |
the `Maybe` type **is** a monad. This led me to explore what makes | |
`Maybe` a monad and write some simple code to make it more apparent to | |
me. | |
Let's look at `Maybe`: | |
```haskell | |
ghci> :info Maybe | |
data Maybe a = Nothing | Just a -- Defined in `Data.Maybe' | |
instance Monad Maybe -- Defined in `Data.Maybe' | |
-- ... other instances follow | |
``` | |
For the sake of exposition, I'll define my own `Maybe` called | |
`Perhaps`. `Nought` is the equivalent of `Nothing`, and `Only` is the | |
equivalent of `Just`. | |
> data Perhaps a = Nought | Only a deriving (Show) | |
A `Monad` is a type that implements two functions (actually, in | |
Haskell, it's four): `bind` and `return`. | |
Looking at the definition of `Monad`: | |
```haskell | |
class Monad m where | |
(>>=) :: m a -> (a -> m b) -> m b | |
(>>) :: m a -> m b -> m b | |
return :: a -> m a | |
fail :: String -> m a | |
-- Defined in `GHC.Base' | |
``` | |
The two easiest functions to implement are `fail` and `return`. | |
`fail` takes an error string and returns the equivalent of `Nothing.` | |
> perhapsFail :: String -> Perhaps a | |
> perhapsFail s = Nought | |
`return` is one of the important Monad functions. It takes a value of | |
type `a` and wraps it in the monad. Given the fact that we always have | |
a value for `return`, it never returns `Nought`. | |
> perhapsReturn :: a -> Perhaps a | |
> perhapsReturn x = Only x | |
The `(>>=)` operator is the Haskell `bind` operator. This is a bit | |
more tricky. It's type is `Monad m => m a -> (a -> m b) -> m b`. It's | |
used to **compose** monadic functions. Here is its definition: | |
> perhapsBind :: Perhaps a -> (a -> Perhaps b) -> Perhaps b | |
> perhapsBind Nought _ = Nought | |
> perhapsBind (Only x) f = f x | |
It takes a `Perhaps` value, and a function for transforming a value of | |
type `a` into a monad with type `b`. | |
The `(>>)` operator sequentially composes two monadic functions, | |
ignoring the result of the first action. | |
> perhapsShift :: Perhaps a -> Perhaps b -> Perhaps b | |
> perhapsShift _ x = x | |
Now, we can make `Perhaps` an instance of the `Monad` type class. | |
> instance Monad Perhaps where | |
> (>>=) = perhapsBind | |
> (>>) = perhapsShift | |
> return = perhapsReturn | |
> fail = perhapsFail | |
There are two rules for monads: they must be associative and they must | |
have an identity function. Due to Haskell's polymorphism, the identity | |
function is already provided. Associativity means that | |
```haskell | |
f x >>= g >>= h | |
``` | |
should be the same as | |
```haskell | |
(f x >>= g) >>= h | |
``` | |
should be the same as | |
```haskell | |
f x >>= (g >>= h) | |
``` | |
Let's see this in action. The first function we'll send it to is a | |
function that returns `Nought` if the number is odd, and `Only n` if | |
`n` is even. | |
> perhapsEven :: Integer -> Perhaps Integer | |
> perhapsEven n | |
> | even n = Only n | |
> | odd n = Nought | |
Here are some examples of `perhapsEven`: | |
```haskell | |
ghci> map perhapsEven [0..5] | |
[Only 0, | |
Nought, | |
Only 2, | |
Nought, | |
Only 4, | |
Nought] | |
``` | |
The next function returns a string of the number if it's given a | |
number that can be divided by (the number isn't zero): | |
> perhapsDividable :: Integer -> Perhaps String | |
> perhapsDividable 0 = Nought | |
> perhapsDividable n = Only (show n) | |
Some examples: | |
```haskell | |
ghci> map perhapsDividable [0..5] | |
[Nought, | |
Only "1", | |
Only "2", | |
Only "3", | |
Only "4", | |
Only "5"] | |
``` | |
We can't just compose `perhapsEven` and `perhapsDividable`; their type | |
would be | |
```haskell | |
(Integer -> Perhaps Integer) -> (Integer -> Perhaps String) | |
``` | |
The types don't line up. Instead, | |
> perhapsShowEvenDividable :: Integer -> Perhaps String | |
> perhapsShowEvenDividable n = perhapsEven n >>= perhapsDividable | |
`perhapsEven` has to be applied first to get to the `Perhaps` type, | |
then functions can be applied. | |
> perhapsStringLength :: String -> Perhaps Int | |
> perhapsStringLength s = Only (length s) | |
After the first application, we can compose an arbitrary number of | |
functions (provided the types match up): | |
```haskell | |
ghci> perhapsEven 3 >>= perhapsDividable >>= perhapsStringLength | |
Nought | |
ghci> perhapsEven 4 >>= perhapsDividable >>= perhapsStringLength | |
Only 1 | |
``` | |
So how is this useful? | |
Imagine `Perhaps` conveys whether a function with side effects | |
succeeded. Let's consider a pair of functions, `perhapsReadFile` and | |
`perhapsParseWidget`. | |
```haskell | |
perhapsReadFile :: FilePath -> Perhaps String | |
perhapsParseWidget :: String -> Perhaps Widget | |
``` | |
`perhapsReadFile` either returns `Only contents` if it could read the | |
contents of the file, and `Nought` if nothing could be | |
read. `perhapsParseWidget` returns `Only w` if the string contains a | |
valid representation of a widget, and `Nought` otherwise. | |
In Go, we might write something like: | |
```go | |
func readAndUseWidget(fileName string) (w widget.Widget, err error) { | |
contents, err := ioutil.ReadFile(fileName) | |
if err != nil { | |
return nil, err | |
} | |
w, err := widget.Unmarshal(contents) | |
if err != nil { | |
return nil, err | |
} | |
// do some initial actions on the widget | |
return w, nil | |
} | |
``` | |
In Haskell, it's | |
```haskell | |
readAndUseWidget :: FilePath -> Perhaps Widget | |
readAndUseWidget path = perhapsReadFile path >>= perhapsParseWidget | |
>>= perhapsInitWidget | |
``` | |
It's much more concise, and the error handling can be abstracted out | |
of view. Monads allow us to safely abstract some of this away. | |
One useful benefit of this is allowing continued execution in the face | |
of failures. | |
If our `readAndUseWidget` wasn't using a monad, and we ran through a | |
sequence of files to process some widgets, an error would stop us dead | |
in our tracks. As an example, the following function will die if an | |
error occurs: | |
```haskell | |
readAndProcessWidgets :: [FilePath] -> [Widget] | |
readAndProcessWidgets = map readAndUseWidget | |
``` | |
Given some paths to widget files, where `widget2.w` doesn't exist: | |
```haskell | |
ghci> readAndProcessWidgets ["widget1.w", "widget2.w", "widget3.w"] | |
[Widget Foo,*** Exception: widget2.w: openFile: does not exist (No | |
such file or directory) | |
``` | |
We don't even get a complete list of widgets back. With the monad | |
version, we'd get | |
```haskell | |
ghci> map readAndUseWidget ["widget1.w", "widget2.w", "widget3.w"] | |
ghci> [Only Widget Foo, Nought, Only Widget Baz] | |
``` | |
We get a list back, and can process the widgets that were successfully | |
parsed. | |
We've uesd the monad here to represent failure, but they can be used | |
to add any additional information to a result besides the pure | |
result. The `IO` monad carries information noting that the world the | |
Haskell program has been run in is unstable; the `State` monad carries | |
some notion of state (state is anathema to functional | |
programming). They carry information about computational | |
dependencies—knowing that the world has changed, or the | |
possibility that a component failed—through a sequence of | |
computations. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment