Skip to content

Commit 11de1c8

Browse files
committed
more on lift and liftIO
fix link to point to latest on stackage add note about equivalence between lift and liftIO add example demonstrating the usefulness of liftIO
1 parent 3d0af7e commit 11de1c8

File tree

1 file changed

+41
-4
lines changed

1 file changed

+41
-4
lines changed

content/monad-transformers.md

Lines changed: 41 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -31,11 +31,11 @@ An `Either e a` wrapped in any other monad, i.e. `m (Either e a)`
3131

3232
## Simple examples of usage
3333

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`.
3535

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`.
3737

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
3939

4040
`MonadTrans` defines one method, `lift`, the signature of which is
4141

@@ -45,6 +45,17 @@ lift :: Monad m => m a -> t m a
4545

4646
Given a monad `m`, we can "lift" into a constructed monad transformer `t` so long as `t` is an instance of `MonadTrans`
4747

48+
### MonadIO
49+
50+
`MonadIO` defines one method, `liftIO`, the signature of which is
51+
52+
```haskell
53+
liftIO :: IO a -> m a
54+
```
55+
56+
`liftIO` allows us to lift an IO action into a transformer stack that is built on top of IO and it works no matter how deeply nested the stack is. We'll see some examples of this below.
57+
58+
4859
Examples:
4960

5061
### MaybeT
@@ -70,7 +81,33 @@ getPassword = do
7081
return password
7182
```
7283

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.
89+
90+
```haskell
91+
getPassword' :: MaybeT (ExceptT MyPasswordError IO) String
92+
getPassword' = do
93+
password <- liftIO getLine
94+
guard (isValid password)
95+
return password
96+
```
97+
98+
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.
74111

75112

76113
* More transformer usage examples

0 commit comments

Comments
 (0)