You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: content/monad-transformers.md
+152-4Lines changed: 152 additions & 4 deletions
Original file line number
Diff line number
Diff line change
@@ -15,21 +15,169 @@ The following is a list of some basic transformers:
15
15
16
16
### MaybeT
17
17
18
-
A `Maybe a` wrapped in any other monad, i.e. `m (Maybe a)
18
+
A `Maybe a` wrapped in any other monad, i.e. `m (Maybe a)`
19
19
20
20
### ReaderT
21
21
22
22
A `Reader r a` in which the resulting `a` is wrapped in any other monad, i.e. `r -> m a`
23
23
24
24
### StateT
25
25
26
-
A `State s a` in which the return value `a`and state`s` are wrapped in any other monad, i.e. `s -> m (a, s)`
26
+
A `State s a` in which the return value and state, namely `(a, s)`, are wrapped in any other monad, i.e. `s -> m (a, s)`
27
27
28
-
### EitherT
28
+
### ExceptT
29
29
30
30
An `Either e a` wrapped in any other monad, i.e. `m (Either e a)`
31
31
32
-
* Simple examples of usage
32
+
## Simple examples of usage
33
+
34
+
[transformers](https://www.stackage.org/package/transformers) is a widely used package which provides transformer versions of various monads. It also provides two useful classes, `MonadTrans` and `MonadIO`.
35
+
36
+
Instances of `MonadTrans` are transformers which can be applied to other monads to create new monads. All of the transformers defined in the `transformers` package are instances of `MonadTrans`.
37
+
38
+
### MonadTrans
39
+
40
+
`MonadTrans` defines one method, `lift`, the signature of which is
41
+
42
+
```haskell
43
+
lift::Monadm=>ma->tma
44
+
```
45
+
46
+
Given a monad `m`, we can "lift" into a constructed monad transformer `t` so long as `t` is an instance of `MonadTrans`
47
+
48
+
### MonadIO
49
+
50
+
`MonadIO` defines one method, `liftIO`, the signature of which is
51
+
52
+
```haskell
53
+
liftIO::IOa->ma
54
+
```
55
+
56
+
`liftIO` allows us to lift an IO action into a transformer stack that is built on top ofIOand it works no matter how deeply nested the stack is.We'll see some examples of this below.
57
+
58
+
59
+
Examples:
60
+
61
+
### MaybeT
62
+
63
+
```haskell
64
+
importControl.Monad
65
+
importControl.Monad.Trans.Maybe
66
+
importControl.Monad.Trans.Class
67
+
68
+
main =do
69
+
password <- runMaybeT getPassword
70
+
case password of
71
+
Just p ->putStrLn"valid password!"
72
+
Nothing->putStrLn"invalid password!"
73
+
74
+
isValid::String->Bool
75
+
isValid = (>=10) .length
76
+
77
+
getPassword::MaybeTIOString
78
+
getPassword =do
79
+
password <- lift getLine
80
+
guard (isValid password)
81
+
return password
82
+
```
83
+
84
+
In this example, we combine the `IO` and `Maybe` monads. `lift getLine` allows us to embed the `IO` action into the `MaybeT` transformer, yielding a value of type `MaybeT IO String`.
85
+
86
+
Note that in this particular example, the use of `lift` in `lift getLine` is equivalent to `liftIO getLine` since we have a one-layer transformer on top of `IO`.
87
+
88
+
### lift vs liftIO
89
+
90
+
Here's a (somewhat contrived) example that demonstrates the difference between `lift` and `liftIO` and the usefulness of the latter.
91
+
92
+
Suppose we added another layer to our transformer stack so that, instead of `MaybeT IO String`, we had `MaybeT (ExceptT MyPasswordError IO) String`.
93
+
94
+
As in our first example, we'd like to lift the `getLine` action into our transformer. Let's try.
In this example, we can use `lift` to go from `IO` into our transformer. But with a deeper stack, we run into problems:
122
+
123
+
```
124
+
> type MyDeeperStack = ReaderT Int (WriterT String IO) Bool
125
+
> :t \x -> (lift x :: MyDeeperStack)
126
+
\x -> (lift x :: MyDeeperStack)
127
+
:: WriterT String IO Bool -> MyDeeperStack
128
+
```
129
+
130
+
In other words, the `m` from `lift :: m a -> t m a` in our `MyDeeperStack` is `WriterT String IO`. So we would to need `lift`*again* in order to go from `IO Bool -> MyDeeperStack`, i.e.
131
+
132
+
```
133
+
> :t \x -> ((lift . lift) x :: MyDeeperStack)
134
+
\x -> ((lift . lift) x :: MyDeeperStack)
135
+
:: IO Bool -> MyDeeperStack
136
+
```
137
+
138
+
This is where `liftIO` helps us. It essentially lets us do a variable number of lifts. This lets us write less brittle code because if we decided to add yet another layer to our transformer stack, we wouldn't have to hardcode another call to `lift`.
139
+
140
+
As an example, what happens if we add a `MaybeT` to our stack?
0 commit comments