返回do块的情况

时间:2017-07-31 16:35:29

标签: haskell functional-programming monads

所以我是Haskell的新手,可能会问一个非常愚蠢的问题。 我尝试运行以下代码:

asdf :: Maybe a -> Maybe a
asdf k = do
    return $ case k of 
                Nothing -> Nothing
                Just x -> Just x

我知道这基本上是fmap,并且只返回是没有意义的。我实际想做的是以下

vote :: PostId -> Bool -> Maybe (Int, Int)
vote id v = do
  p <- runSQL $ P.get id
  return $ case p of
    Nothing -> Nothing
    Just p -> do
                let uv = postUpvotes p
                let dv = postDownvotes p
                let nuv = if v then uv + 1 else uv
                let ndv = if not v then dv + 1 else dv
                runSQL $ update id [PostUpvotes =. nuv, PostDownvotes =. ndv]
                (nuv, ndv)

我想这有不止一个问题。我的第一个例子究竟出了什么问题?第二个问题的解决方法是完全错误还是由此引发的?你会怎么写的?

1 个答案:

答案 0 :(得分:2)

您的第一个代码示例不需要return

你有没有想出Haskell中return的含义?它与任何其他编程语言中的return不同。在大多数语言中,它是一个控制语句流,它将控制权返回给调用函数并附加一个值。在Haskell中,它只是一个具有误导性名称的函数。

在这种情况下,你应该说:

asdf k =
    case k of 
            Nothing -> Nothing
            Just x -> Just x

在Haskell中do表示法与monad一起使用,如IO。令人困惑的是,Maybe实际上也是一个单子。事实上,它是最简单的monad。当你想要一个在某种环境中运行的菊花链函数时,你只需要使用monad,这不是这里的情况。

转到第二个示例,问题是runSQL必须与外部世界进行交互,因此对于某些类型IO a,它几​​乎肯定会返回a类型的内容。在这种情况下, 在外部世界的上下文中执行菊花链函数,因此使用monad是必要的。

return是一个函数,在这种情况下,具有类型

return :: a -> IO a

它应该被称为pure,因为它需要一个纯值并将其包含在monadic上下文中。 (当您使用Applicative Functors时,您会发现一个名为return的{​​{1}}版本。

关于monad的规则是,一旦你处于monadic环境中,你就无法离开;您可以对上下文中的值执行纯计算,但结果仍保留在monadic上下文中。该上下文由类似&#34; IO&#34;表示。所以在这种情况下你的函数类型应该是

pure

然而,如果你尝试,你会发现它仍然无法正常工作。 vote :: PostId -> Bool -> IO (Maybe (Int, Int)) 的第一个分支很好,因为它会返回case。但是第二个分支有一个类型错误,因为它尝试在IO操作后返回Nothing。您已尝试将(nuv, nvd)分解出来,但实际上您需要在第一个分支中return,然后在第二个分支结束时return Nothing

顺便说一句,您不需要为每个值添加新的return $ Just (nuv, nvd)。你可以说

let