无法返回可能导致IO Monad

时间:2019-03-01 14:02:51

标签: haskell io-monad

我不明白为什么这段代码示例不起作用,在RWH书中它起作用了:

module Monads where 
    import Data.Maybe
    import Control.Monad
    amap=[("a",1),("bb",2)]
    bmap=[(1,100),(2,200)]
    final=[(100,1000),(200,2000)]

    f::String->IO (Maybe Int)
    f par=do
          a<-lookup par amap
          b<-lookup a bmap
          lookup b final

它不起作用,而且我不知道它如何工作,因为最后一行返回Maybe something而不是IO something

我也尝试将最后一行更改为:
return (lookup b final)
我认为应该是完美的(查找返回Maybe Int,然后用return包装)

使用return

时出现以下错误
* Couldn't match type `Maybe' with `IO'
      Expected type: IO Integer
        Actual type: Maybe Integer
    * In a stmt of a 'do' block: b <- lookup a bmap
      In the expression:
        do a <- lookup par amap
           b <- lookup a bmap
           return (lookup b final)
      In an equation for `f':
          f par
            = do a <- lookup par amap
                 b <- lookup a bmap
                 return (lookup b final)
   |
11 |           b<-lookup a bmap
   |              ^^^^^^^^^^^^^

1 个答案:

答案 0 :(得分:4)

对于给定的类型签名,您需要将对lookup的最终调用返回的值提升为IO的值。

f::String->IO (Maybe Int)
f par = return $ do
      a <- lookup par amap
      b <- lookup a bmap
      lookup b final

do表达式是在>>= monad中操作的Maybe的语法糖,在这种情况下不是IO,而是return接受结果Maybe Int的值并产生所需的IO (Maybe Int)的值。可以改成

f par = return (lookup par amap >>= \a -> lookup a bmap >>= \b -> lookup b final)

但是,除了强迫您使用return的类型签名之外,关于f的其他所有内容要求要求它返回一个IO值;它的参数或三个关联列表中的任何一个都不以任何方式涉及IO,因此您可以简单地更改类型签名以消除对IO的任何引用,而仅在Maybe monad中进行操作。

f :: String -> Maybe Int
f par = do
    a <- lookup par amap
    b <- lookup a bmap
    lookup b final

旁白:摆脱do

由于您将对lookup的各种调用链接在一起,因此,如果可以将每个结果以无点样式馈送到下一个调用,那就太好了。为此,您需要颠倒lookup接受其参数的顺序。使用flipflip lookup :: [(a,b)] -> a -> Maybe b可以做到这一点。

f :: String -> Maybe Int
f par = let lookup' = flip lookup
        in lookup' amap par >>= lookup' bmap >>= lookup' final

更进一步,您可以使用从par导入的>=>运算符完全删除对Control.Monad的引用。将其类型与>>=的类型进行比较:

:t (>>=)
(>>=) :: Monad m => m a -> (a -> m b) -> m b
:t (>=>)
(>=>) :: Monad m => (a -> m b) -> (b -> m c) -> a -> m c

撰写调用Maybe,而不是以lookup'值开头,并将起始字符串提供给函数。

f :: String -> Maybe Int
f = let lookup' = flip lookup
     in lookup' amap >=> lookup' bmap >=> lookup' final