如何创建允许 IO 但不是 MonadIO 的 monad?
How to create a monad which allows IO but is NOT a MonadIO?
我正在尝试创建一个只允许特定 IO 函数的 monad。这意味着这个假设的 monad 不能是 MonadIO
并且不能允许 liftIO
被调用。
这是我到目前为止所拥有的,但我仍然坚持 AppM
的 Monad
实例:
data AppM a = AppM {unwrapAppM :: ReaderT Env (LoggingT IO) a}
instance Functor AppM where
fmap fn appm = AppM $ fmap fn (unwrapAppM appm)
instance Applicative AppM where
pure a = AppM $ pure a
如果您只是想隐藏 AppM
的 MonadIO
-ness
我会继续并放入一个
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
并将 data
声明更改为
newtype App a = App {runApp :: ReaderT Env (LoggingT IO) a}
deriving (Functor, Applicative, Monad, MonadReader Env,
, MonadLoggerIO }
因此你的 App
不是 MonadIO
如果你需要 liftIO
喜欢的动作你可以在你的库中提供那些
putStrLn :: String -> App ()
putStrLn = fmap App . liftIO Prelude.putStrLn
注意:liftIO
用于 ReaderT Env (LoggingT IO) ()
,然后包装到 App
中,您不会公开完整的 IO 功能。
更新
关于如何实现 Functor
、Applicative
和 Monad
的问题,这只是 wrapping/unwrapping:
的任务
instance Functor App where
fmap f = App . fmap f . runApp
instance Applicative App where
pure = App . pure
mf <*> mx = App (runApp mf <*> runApp mx)
instance Monad App where
mx >>= f = App $ (runApp mx) >>= (runApp . f)
最后一行是唯一棘手的一行 - 因为
>>= :: ReaderT Env (LoggingT IO) a -> (a -> ReaderT Env (LoggingT IO) b) -> ReaderT Env (LoggingT IO) b
但是 mx :: App a
和 f :: a -> App b
所以我们需要
runApp :: App a -> ReaderT Env (LoggingT IO) a
解包 f
的结果类型以在解包设置中工作 - 这在写下来时看起来非常明显,但在此之前可能会引起一些头痛。
更新2
我发现有人从 monad reader Ed Z. Yang 链接了很久很久(但在同一个星系)的论文 -
三个 Monad(逻辑、提示、失败)
我正在尝试创建一个只允许特定 IO 函数的 monad。这意味着这个假设的 monad 不能是 MonadIO
并且不能允许 liftIO
被调用。
这是我到目前为止所拥有的,但我仍然坚持 AppM
的 Monad
实例:
data AppM a = AppM {unwrapAppM :: ReaderT Env (LoggingT IO) a}
instance Functor AppM where
fmap fn appm = AppM $ fmap fn (unwrapAppM appm)
instance Applicative AppM where
pure a = AppM $ pure a
如果您只是想隐藏 AppM
MonadIO
-ness
我会继续并放入一个
{-# LANGUAGE GeneralizedNewtypeDeriving #-}
并将 data
声明更改为
newtype App a = App {runApp :: ReaderT Env (LoggingT IO) a}
deriving (Functor, Applicative, Monad, MonadReader Env,
, MonadLoggerIO }
因此你的 App
不是 MonadIO
如果你需要 liftIO
喜欢的动作你可以在你的库中提供那些
putStrLn :: String -> App ()
putStrLn = fmap App . liftIO Prelude.putStrLn
注意:liftIO
用于 ReaderT Env (LoggingT IO) ()
,然后包装到 App
中,您不会公开完整的 IO 功能。
更新
关于如何实现 Functor
、Applicative
和 Monad
的问题,这只是 wrapping/unwrapping:
instance Functor App where
fmap f = App . fmap f . runApp
instance Applicative App where
pure = App . pure
mf <*> mx = App (runApp mf <*> runApp mx)
instance Monad App where
mx >>= f = App $ (runApp mx) >>= (runApp . f)
最后一行是唯一棘手的一行 - 因为
>>= :: ReaderT Env (LoggingT IO) a -> (a -> ReaderT Env (LoggingT IO) b) -> ReaderT Env (LoggingT IO) b
但是 mx :: App a
和 f :: a -> App b
所以我们需要
runApp :: App a -> ReaderT Env (LoggingT IO) a
解包 f
的结果类型以在解包设置中工作 - 这在写下来时看起来非常明显,但在此之前可能会引起一些头痛。
更新2
我发现有人从 monad reader Ed Z. Yang 链接了很久很久(但在同一个星系)的论文 - 三个 Monad(逻辑、提示、失败)