如何创建一个允许IO但不是MonadIO的monad?

Sau*_*nda 3 monads haskell monad-transformers

我正在尝试创建一个只允许特定IO功能的monad.这意味着这个假设的monad不能是a MonadIO并且不能被允许liftIO被调用.

这是我到现在所拥有的,但我坚持使用以下Monad实例AppM:

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
Run Code Online (Sandbox Code Playgroud)

eps*_*lbe 9

如果你只是想隐藏MonadIO你的性能AppM

我会继续并投入

{-# LANGUAGE GeneralizedNewtypeDeriving #-}
Run Code Online (Sandbox Code Playgroud)

并将data声明更改为

newtype App a = App {runApp :: ReaderT Env (LoggingT IO) a}
              deriving (Functor, Applicative, Monad, MonadReader Env,
                       , MonadLoggerIO }
Run Code Online (Sandbox Code Playgroud)

因此,如果您需要喜欢可以提供图书馆内部内容的操作,那么您App就不是MonadIOliftIO

putStrLn :: String -> App ()
putStrLn = fmap App . liftIO Prelude.putStrLn
Run Code Online (Sandbox Code Playgroud)

注:liftIO对于IS ReaderT Env (LoggingT IO) (),然后包装到App了,你不要暴露充分IO能力.

更新

至于问题如何落实Functor,ApplicativeMonad它仅仅是包装/解包的单纯任务:

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)
Run Code Online (Sandbox Code Playgroud)

最后一行是唯一棘手的 - 作为

>>= :: ReaderT Env (LoggingT IO) a -> (a -> ReaderT Env (LoggingT IO) b) -> ReaderT Env (LoggingT IO) b
Run Code Online (Sandbox Code Playgroud)

mx :: App af :: a -> App b所以我们需要

runApp :: App a -> ReaderT Env (LoggingT IO) a
Run Code Online (Sandbox Code Playgroud)

打开结果类型f以在打开的设置中工作 - 这在写下来时似乎非常明显,但在此之前可能会引起一些麻烦.

UPDATE2

我发现那篇论文有人把我和很长时间联系起来(但在同一个星系中)来自monad读者Ed Z. Yang - Three Monads(逻辑,提示,失败)

  • 也许这是非常明显的(但为了未来读者可能不知道它的利益) - 为了以这种方式隐藏`App`的`IO`-ness,不能导出`App`构造函数(即` App`必须是抽象类型); 并且你必须(在GHC的最新版本上)向`App`添加角色注释,或者`Data.Coerce.coerce`允许即使在未导出构造函数时也提取内部表示. (2认同)