Haskell 如何创建允许IO但不是MonadIO的monad?
我试图创建一个只允许特定IO函数的monad。这意味着这个假设的monad不能是Haskell 如何创建允许IO但不是MonadIO的monad?,haskell,monads,monad-transformers,Haskell,Monads,Monad Transformers,我试图创建一个只允许特定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 Ap
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 #-}
并将数据
声明更改为
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)
但是mx::App a
和f::a->App b
所以我们需要
runApp :: App a -> ReaderT Env (LoggingT IO) a
打开f
的结果类型以在“打开”设置中工作-这在写下时似乎非常明显,但在此之前可能会引起一些头痛
更新2
我发现有人把我和monad reader联系了很长一段时间(但在同一个银河系中)我已经有一段时间没有问过类似的问题了,所以我不是100%确定,但有人建议看一下。你可以将IO动作定义为GADT,并像解释器一样执行它们,因此,将io限制在您想要的范围内,您可以使用Free
执行此操作。另一种选择是使用mtl
风格的api。你想到了哪些特定的函数?更大的想法是——使用类型类约束只允许特定的IO功能,这样一个函数的签名会立即告诉读者它在做什么类型的IO。太棒了。但是,就我的理解而言,Monad实例的代码在本例中是什么样子的?@SaurabhNanda-updated-这就是您要寻找的吗?是的。似乎是我需要的。让我在笔记本电脑上试一下,然后再回来找你。也许这是非常明显的(但为了将来可能不知道的读者的利益)-为了以这种方式隐藏App
的IO
,必须不导出App
构造函数(即App
必须是抽象类型);而且您必须(在GHC的最新版本上)向应用程序
或数据添加角色注释。强制。强制
允许在构造函数未导出时提取内部表示。
runApp :: App a -> ReaderT Env (LoggingT IO) a