代码之家  ›  专栏  ›  技术社区  ›  Saurabh Nanda

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

  •  3
  • Saurabh Nanda  · 技术社区  · 9 年前

    我试图创建一个只允许特定IO函数的单子。这意味着这个假设的单子不能是 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
    
    1 回复  |  直到 9 年前
        1
  •  9
  •   Peter Hall    9 年前

    如果你只是想隐藏 MonadIO -您的 AppM

    {-# LANGUAGE GeneralizedNewtypeDeriving #-}
    

    并更改 data

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

    因此 App 是不是 如果你需要的话 liftIO 就像你可以在你的库中提供的动作一样

    putStrLn :: String -> App ()
    putStrLn = fmap App . liftIO Prelude.putStrLn
    

    注:the 是用于 ReaderT Env (LoggingT IO) () 然后将其包裹在 ,并且不公开完整的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 在未包装的环境下工作——这在写下来时看起来很明显,但在此之前可能会引起一些头痛。

    更新2

    我发现有人把我和monad阅读器联系了很久(但在同一个星系) Ed Z. Yang - Three Monads (Logic, Prompt, Failure)