我目前正在尝试学习Haskell,我真的无法理解在do-block中只使用一个monad的概念.如果我有foo :: Int -> Maybe Int并且想hIsEOF :: Handle -> IO Bool在这个函数中使用例如函数.有人可以在一些基本的例子上向我解释我将如何使用hIsEOF并可以某种方式使用Bool?
我一直试图在这里和谷歌搜索,但我总是遇到一些高级的东西,基本上没有人解释如何,他们只是给出如何适应OP的代码的建议.我看到那些线程中提到的monad变换器,但即使在阅读了一些资源之后,我似乎也找不到如何使用它们的正确方法.
beh*_*uri 12
使用monad变形金刚,您需要做的就是
将函数签名更改Int -> Maybe Int为
foo :: Int -> MaybeT IO Int
Run Code Online (Sandbox Code Playgroud)解除do块内的所有IO操作(或者liftIO在这种情况下).看看为什么你需要这个提升,它究竟是什么.
使用运行该功能 runMaybeT
一个最小的例子是:
import Control.Monad.Trans (lift)
import Control.Monad.Trans.Maybe (MaybeT, runMaybeT)
import System.IO (openFile, hClose, hSeek, hIsEOF)
import System.IO (IOMode(ReadMode), SeekMode(AbsoluteSeek))
foo :: Int -> MaybeT IO Int
foo i = do
h <- lift $ openFile "test.txt" ReadMode
-- move the handle i bytes ahead
lift . hSeek h AbsoluteSeek $ fromIntegral i
eof <- lift $ hIsEOF h -- check if hit end of file
lift $ hClose h
if eof then fail "eof!" else return i
Run Code Online (Sandbox Code Playgroud)
然后,
\> runMaybeT $ foo 1
Just 1
\> runMaybeT $ foo 100 -- would hit eof
Nothing
Run Code Online (Sandbox Code Playgroud)
你得到的是这种类型:
(runMaybeT . foo) :: Int -> IO (Maybe Int)
Run Code Online (Sandbox Code Playgroud)
最简洁的答案是不.记谱法基于两件事
return :: a -> m a
>>= :: m a -> (a -> m b) -> m b
Run Code Online (Sandbox Code Playgroud)
请注意>>=,尽管您可以使用两种不同的内部类型(a和b),但它只适用于一种外部类型,一种是monad(m).这两个m a和a -> m b是相同的单子.
答案越长,你必须将它们转换为相同的monad.例如,Maybe可以转换为IO如此:
maybeToIO Nothing = error "No thing"
maybeToIO (Just a) = return a
Run Code Online (Sandbox Code Playgroud)
一般来说,Monads不能相互转换,特殊情况除外.
那么为什么>>=只与一个monad一起工作呢?好吧,看看这个.它被定义为一次使用单个monad,并且定义了do-notation >>=.选择这个定义的原因有点复杂,但如果有人想要,我可以编辑它.
你可以想出你自己的>>=多个monad,然后使用可重新绑定的语法,但这可能很难.