【发布时间】:2018-08-18 08:46:40
【问题描述】:
我有一个 * -> * 类型的 Logger 类型,它可以采用任何类型并将值记录在文件中。我试图以一种单子的方式实现这一点,以便我登录并继续工作。我的代码看起来像
import Control.Applicative
import Control.Monad
import System.IO
import Control.Monad.IO.Class
instance Functor Logger where
fmap = liftM
instance Applicative Logger where
pure = return
(<*>) = ap
newtype Logger a = Logger a deriving (Show)
instance Monad (Logger) where
return = Logger
Logger logStr >>= f = f logStr
instance MonadIO (Logger) where
liftIO a = do
b <- liftIO a
return b
logContent :: (Show a) => a -> Logger a
logContent a = do
b <- liftIO $ logContent2 a
return b
logContent2 :: (Show a) => a -> IO a
logContent2 a = do
fHandle <- openFile "test.log" AppendMode
hPrint fHandle a
hClose fHandle
return (a)
liftIO 函数在调用自身时进行无限循环。我也不能做 b
【问题讨论】:
-
liftIO a = do { b <- liftIO a; ...liftIO 作为参数本身。因此是无限循环。 -
我认为
MonadIO不像你认为的那样有效。看看all defined instances 的MonadIO。看到图案了吗?除了IO本身之外的所有实例都是转换器,为了成为MonadIO,它们需要转换MonadIO。 IOW 如果您希望您的 monad 成为MonadIO,则它需要在内部某处直接或间接包含实际的IO a值。否则你无法从IO a提升到Logger a,因为你永远无法逃脱IO。 -
但是如果我想从一种 monad 类型转换为另一种呢?这将帮助我从一个单子链接到另一个单子以创建流程?
-
您需要首先决定 Logger 将要做什么,而 IO 还没有做。例如,它是否应该携带日志文件的文件句柄?一旦您知道设计将开始变得更加清晰。
标签: haskell monads monad-transformers io-monad