一个do-block中有多个monad

时间:2016-02-24 02:59:24

标签: haskell

我目前正在尝试学习Haskell,我真的无法理解在do-block中只使用一个monad的概念。如果我有foo :: Int -> Maybe Int并希望在此函数中使用例如函数hIsEOF :: Handle -> IO Bool。有人可以在一些基本示例中向我解释如何使用hIsEOF并以某种方式使用Bool

我一直在尝试在这里和谷歌搜索,但我总是遇到一些高级的东西,基本上没有人解释如何,他们只是提供如何适应OP的代码的建议。我看到那些线程中提到的monad变换器,但即使在阅读了一些资源之后,我似乎找不到如何使用它们的正确方法。

2 个答案:

答案 0 :(得分:12)

使用monad变换器,您需要做的就是

  1. 将函数签名从Int -> Maybe Int更改为

    foo :: Int -> MaybeT IO Int
    
  2. lift do块内的所有IO操作(在这种情况下为liftIO)。请参阅here why you need this lifting and what exactly it does

  3. 使用runMaybeT

  4. 运行该功能

    最小的例子是:

    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
    

    然后,

    \> runMaybeT $ foo 1
    Just 1
    \> runMaybeT $ foo 100  -- would hit eof
    Nothing
    

    你从中得到的将是以下类型:

    (runMaybeT . foo) :: Int -> IO (Maybe Int)
    

答案 1 :(得分:7)

简短的回答是否定的。记谱法基于两件事

return :: a -> m a
>>= :: m a -> (a -> m b) -> m b

请注意>>=虽然您可以使用两种不同的内部类型(ab),但它只适用于一种外部类型,一种monad(m )。 m aa -> m b都是同一个monad。

答案越长,你必须将它们转换为同一个monad。例如,Maybe可以像这样转换为IO

maybeToIO Nothing = error "No thing"
maybeToIO (Just a) = return a

一般情况下,Monads不能相互转换,除非在特殊情况下。

那么为什么>>=只适用于一个monad?那么看看this。它是定义的,以便一次使用单个monad,并且定义符号以使用>>=。选择这个定义的原因有点复杂,但如果有人想要,我可以编辑它。

您可以提出自己的>>=,它适用于多个monad,然后使用rebindable syntax,但这可能很难。