我正在尝试创建一个只允许特定IO功能的monad。这意味着这个假设的monad不能是MonadIO
,也不能允许调用liftIO
。
以下是我到目前为止所做的事情,但我仍然坚持Monad
的{{1}}个实例:
AppM
答案 0 :(得分:9)
如果您只想隐藏MonadIO
AppM
-
我会继续并加入
{-# 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
的问题,这只是包装/解包的任务:
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
的结果类型以在解包设置中工作 - 这在写下来时似乎非常明显,但在此之前可能会引起一些麻烦。
我发现那篇文章有人把我和很长时间联系起来(但在同一个星系中)来自monad读者Ed Z. Yang - Three Monads (Logic, Prompt, Failure)