1{-# LANGUAGE RankNTypes #-}
2-- | Please see the README.md file for information on using this
3-- package at <https://www.stackage.org/package/unliftio-core>.
4module Control.Monad.IO.Unlift
5  ( MonadUnliftIO (..)
6  , UnliftIO (..)
7  , askUnliftIO
8  , askRunInIO
9  , withUnliftIO
10  , toIO
11  , wrappedWithRunInIO
12  , MonadIO (..)
13  ) where
14
15import Control.Monad.IO.Class
16import Control.Monad.Trans.Reader (ReaderT (..))
17import Control.Monad.Trans.Identity (IdentityT (..))
18
19-- | The ability to run any monadic action @m a@ as @IO a@.
20--
21-- This is more precisely a natural transformation. We need to new
22-- datatype (instead of simply using a @forall@) due to lack of
23-- support in GHC for impredicative types.
24--
25-- @since 0.1.0.0
26newtype UnliftIO m = UnliftIO { unliftIO :: forall a. m a -> IO a }
27
28-- | Monads which allow their actions to be run in 'IO'.
29--
30-- While 'MonadIO' allows an 'IO' action to be lifted into another
31-- monad, this class captures the opposite concept: allowing you to
32-- capture the monadic context. Note that, in order to meet the laws
33-- given below, the intuition is that a monad must have no monadic
34-- state, but may have monadic context. This essentially limits
35-- 'MonadUnliftIO' to 'ReaderT' and 'IdentityT' transformers on top of
36-- 'IO'.
37--
38-- Laws. For any value @u@ returned by 'askUnliftIO', it must meet the
39-- monad transformer laws as reformulated for @MonadUnliftIO@:
40--
41-- * @unliftIO u . return = return@
42--
43-- * @unliftIO u (m >>= f) = unliftIO u m >>= unliftIO u . f@
44--
45-- Instances of @MonadUnliftIO@ must also satisfy the idempotency law:
46--
47-- * @askUnliftIO >>= \\u -> (liftIO . unliftIO u) m = m@
48--
49-- This law showcases two properties. First, 'askUnliftIO' doesn't change
50-- the monadic context, and second, @liftIO . unliftIO u@ is equivalent to
51-- @id@ IF called in the same monadic context as 'askUnliftIO'.
52--
53-- @since 0.1.0.0
54class MonadIO m => MonadUnliftIO m where
55  -- | Convenience function for capturing the monadic context and running an 'IO'
56  -- action with a runner function. The runner function is used to run a monadic
57  -- action @m@ in @IO@.
58  --
59  -- @since 0.1.0.0
60  withRunInIO :: ((forall a. m a -> IO a) -> IO b) -> m b
61instance MonadUnliftIO IO where
62  {-# INLINE withRunInIO #-}
63  withRunInIO inner = inner id
64instance MonadUnliftIO m => MonadUnliftIO (ReaderT r m) where
65  {-# INLINE withRunInIO #-}
66  withRunInIO inner =
67    ReaderT $ \r ->
68    withRunInIO $ \run ->
69    inner (run . flip runReaderT r)
70
71instance MonadUnliftIO m => MonadUnliftIO (IdentityT m) where
72  {-# INLINE withRunInIO #-}
73  withRunInIO inner =
74    IdentityT $
75    withRunInIO $ \run ->
76    inner (run . runIdentityT)
77
78-- | Capture the current monadic context, providing the ability to
79-- run monadic actions in 'IO'.
80--
81-- See 'UnliftIO' for an explanation of why we need a helper
82-- datatype here.
83--
84-- Prior to version 0.2.0.0 of this library, this was a method in the
85-- 'MonadUnliftIO' type class. It was moved out due to
86-- <https://github.com/fpco/unliftio/issues/55>.
87--
88-- @since 0.1.0.0
89askUnliftIO :: MonadUnliftIO m => m (UnliftIO m)
90askUnliftIO = withRunInIO (\run -> return (UnliftIO run))
91{-# INLINE askUnliftIO #-}
92-- Would be better, but GHC hates us
93-- askUnliftIO :: m (forall a. m a -> IO a)
94
95
96-- | Same as 'askUnliftIO', but returns a monomorphic function
97-- instead of a polymorphic newtype wrapper. If you only need to apply
98-- the transformation on one concrete type, this function can be more
99-- convenient.
100--
101-- @since 0.1.0.0
102{-# INLINE askRunInIO #-}
103askRunInIO :: MonadUnliftIO m => m (m a -> IO a)
104-- withRunInIO return would be nice, but GHC 7.8.4 doesn't like it
105askRunInIO = withRunInIO (\run -> (return (\ma -> run ma)))
106
107-- | Convenience function for capturing the monadic context and running
108-- an 'IO' action. The 'UnliftIO' newtype wrapper is rarely needed, so
109-- prefer 'withRunInIO' to this function.
110--
111-- @since 0.1.0.0
112{-# INLINE withUnliftIO #-}
113withUnliftIO :: MonadUnliftIO m => (UnliftIO m -> IO a) -> m a
114withUnliftIO inner = askUnliftIO >>= liftIO . inner
115
116-- | Convert an action in @m@ to an action in @IO@.
117--
118-- @since 0.1.0.0
119{-# INLINE toIO #-}
120toIO :: MonadUnliftIO m => m a -> m (IO a)
121toIO m = withRunInIO $ \run -> return $ run m
122
123{- | A helper function for implementing @MonadUnliftIO@ instances.
124Useful for the common case where you want to simply delegate to the
125underlying transformer.
126
127@since 0.1.2.0
128==== __Example__
129
130> newtype AppT m a = AppT { unAppT :: ReaderT Int (ResourceT m) a }
131>   deriving (Functor, Applicative, Monad, MonadIO)
132>   -- Unfortunately, deriving MonadUnliftIO does not work.
133>
134> instance MonadUnliftIO m => MonadUnliftIO (AppT m) where
135>   withRunInIO = wrappedWithRunInIO AppT unAppT
136-}
137{-# INLINE wrappedWithRunInIO #-}
138wrappedWithRunInIO :: MonadUnliftIO n
139                   => (n b -> m b)
140                   -- ^ The wrapper, for instance @IdentityT@.
141                   -> (forall a. m a -> n a)
142                   -- ^ The inverse, for instance @runIdentityT@.
143                   -> ((forall a. m a -> IO a) -> IO b)
144                   -- ^ The actual function to invoke 'withRunInIO' with.
145                   -> m b
146wrappedWithRunInIO wrap unwrap inner = wrap $ withRunInIO $ \run ->
147  inner $ run . unwrap
148