【问题标题】:Can not return maybe result in the IO Monad无法返回可能导致 IO Monad
【发布时间】:2019-03-01 14:02:51
【问题描述】:

我不明白为什么这个代码示例不起作用,而在 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
   |              ^^^^^^^^^^^^^

【问题讨论】:

  • do 块指的是Maybe,而不是IO。看起来整个东西应该是f par = return $ do ...,或者您可以将类型更改为f::String-&gt;Maybe Int。
  • 你是对的,这永远不会起作用(如果返回类型是Maybe Int,它会起作用)。但是我在您链接到的章节中的任何地方都没有看到该代码 sn-p 。 (实际上我不久前经历了 RWH 的大部分内容——它已经过时了,而且他们使用的许多模块似乎不再使用相同的 API 存在——但我不记得这样的事情了。)
  • 您的代码中没有任何内容表明f 必须返回IO 值。
  • 不,do 适用于 any monad。
  • @RobinZigmond 这就是我的意思;类型签名对返回类型施加了一个外部约束,其参数或它所操作的列表并未建议。

标签: haskell io-monad


【解决方案1】:

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

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

do 表达式是 &gt;&gt;= 使用的语法糖,在 Maybe monad 中运行,在这种情况下不是 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)] -&gt; a -&gt; Maybe b 来做这件事很简单。

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

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

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

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

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

【讨论】:

    猜你喜欢
    • 2021-04-06
    • 1970-01-01
    • 1970-01-01
    • 2018-06-03
    • 1970-01-01
    • 2011-07-08
    • 2018-10-27
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多