@@ -511,3 +511,46 @@ newtype E7 a = E7 ((() -> () -> a) -> a)
511
511
newtype E8 a = E8 ((() -> a -> () ) -> a )
512
512
newtype E9 a = E8 ((() -> () -> () ) -> () )
513
513
```
514
+
515
+ ## Lifting `IO` to `MonadIO`
516
+
517
+ Let's look at something seemingly unrelated to get a feel for the power of our
518
+ new analysis tools. Consider the base function `openFile` :
519
+
520
+ ```haskell
521
+ openFile :: FilePath -> IOMode -> IO Handle
522
+ ```
523
+
524
+ We may want to use this from a monad transformer stack based on top of the `IO `
525
+ monad. The standard approach to that is to use the `MonadIO ` typeclass as a
526
+ constraint, and its `liftIO` function. This is all rather straightforward:
527
+
528
+ ```haskell
529
+ import System.IO
530
+ import Control.Monad.IO.Class
531
+
532
+ openFileLifted :: MonadIO m => FilePath -> IOMode -> m Handle
533
+ openFileLifted fp mode = liftIO (openFile fp mode)
534
+ ```
535
+
536
+ But of course, we all prefer using the ` withFile ` function instead of
537
+ ` openFile ` to ensure resources are cleaned up in the presence of exceptions. As
538
+ a reminder, that function has a type signature:
539
+
540
+ ``` haskell
541
+ withFile :: FilePath -> IOMode -> (Handle -> IO a ) -> IO a
542
+ ```
543
+
544
+ So can we somehow write our lifted version with type signature:
545
+
546
+ ```haskell
547
+ withFileLifted :: MonadIO m => FilePath -> IOMode -> (Handle -> m a ) -> m a
548
+ ```
549
+
550
+ Try as we might, this can't be done, at least not directly (if you're really
551
+ curious, see [lifted- base](http:// www. stackage. org/ package/ lifted- base) and its
552
+ implementation of `bracket` ). And now, we have the vocabulary to explain this
553
+ succinctly: the `IO ` type appears in both positive and negative position in
554
+ `withFile` 's type signature. By contrast, with `openFile` , `IO ` appears
555
+ exclusively in positive position, meaning our transformation function
556
+ (`liftIO` ) can be applied to it.
0 commit comments