所以我是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)
我想这有不止一个问题。我的第一个例子究竟出了什么问题?第二个问题的解决方法是完全错误还是由此引发的?你会怎么写的?
答案 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