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
+41-4Lines changed: 41 additions & 4 deletions
Original file line number
Diff line number
Diff line change
@@ -31,11 +31,11 @@ An `Either e a` wrapped in any other monad, i.e. `m (Either e a)`
31
31
32
32
## Simple examples of usage
33
33
34
-
### MonadTrans
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
35
36
-
[transformers](https://www.stackage.org/lts-3.20/package/transformers-0.4.2.0) is a widely used package which provides transformer versions of various monads.
36
+
`MonadTrans` makes it easy to embed one monad into another. All of the transformers defined in the `transformers` package are instances `MonadTrans`.
37
37
38
-
It also provides a `MonadTrans` class which makes it easy to embed one monad into another. All of the transformers defined in the `transformers` package are instances `MonadTrans`.
38
+
### MonadTrans
39
39
40
40
`MonadTrans` defines one method, `lift`, the signature of which is
41
41
@@ -45,6 +45,17 @@ lift :: Monad m => m a -> t m a
45
45
46
46
Given a monad `m`, we can "lift" into a constructed monad transformer `t` so long as `t` is an instance of `MonadTrans`
47
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
+
48
59
Examples:
49
60
50
61
### MaybeT
@@ -70,7 +81,33 @@ getPassword = do
70
81
return password
71
82
```
72
83
73
-
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`.
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
+
Here's a (somewhat contrived) example that demonstrates the difference between `lift` and `liftIO` and the usefulness of the latter.
In this variation we have more than one layer on top of `IO`. We have a `MaybeT` on top of `ExceptT` on top of `IO`. This is where `liftIO` helps us. We can use use `liftIO` to lift the `getLine` action into our stack no matter how deep `IO` is in our stack.
99
+
100
+
If we tried to use `lift` instead of `liftIO`, we'd see the following error:
101
+
102
+
```
103
+
Couldn't match type ‘IO’ with ‘ExceptT MyPasswordError IO’
104
+
Expected type: ExceptT MyPasswordError IO String
105
+
Actual type: IO String
106
+
In the first argument of ‘lift’, namely ‘getLine’
107
+
In a stmt of a 'do' block: password <- lift getLine
108
+
```
109
+
110
+
The error means we have another layer of our stack that we need to traverse before we can lift the IO action into our stack. In other words, we would need to do `lift (lift getLine)`. This is precisely what `liftIO` gives us. Doing `lift . lift . lift ...` is unmaintainable because it relies on the stack being a specific depth. If we decided to add another monad to our stack, our nested lifting would break. With `liftIO` we can short circuit this and simply life the IO action all the way to the bottom of our stack.
0 commit comments