我不明白为什么这段代码示例不起作用,在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
| ^^^^^^^^^^^^^
答案 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
接受其参数的顺序。使用flip
:flip 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