Return 在 do 块中的情况
Return case of in a do block
所以我是 Haskell 的新手,可能会问一个非常愚蠢的问题。
我尝试将以下代码获取到 运行:
asdf :: Maybe a -> Maybe a
asdf k = do
return $ case k of
Nothing -> Nothing
Just x -> Just x
我知道这基本上是 fmap
并且仅使用 return 是没有意义的。我真正想做的是以下
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)
我想这有不止一个问题。我的第一个例子到底有什么问题?第二个问题的方法是完全错误的还是怎么回事?你会怎么写?
您的第一个代码示例不需要 return
.
你知道return
在Haskell中的意思了吗?它不像任何其他编程语言中的 return
。在大多数语言中,它是一个控制流语句,return 控制带有附加值的调用函数。在 Haskell 中,它只是一个具有误导性名称的函数。
在这种情况下你应该说:
asdf k =
case k of
Nothing -> Nothing
Just x -> Just x
在 Haskell do
中,符号与 monads 一起使用,如 IO。令人困惑的是 Maybe
实际上也是一个 monad。事实上,它是最简单的 monad。只有当你想要在某种上下文中 运行 的菊花链函数时,你才需要使用 monad,而这里不是这种情况。
继续你的第二个例子,问题是 runSQL
必须与外界互动,所以几乎可以肯定 return 是某种类型的 IO a
a
。在这种情况下,您是在外部世界的上下文中执行菊花链功能,因此使用 monad 是必要的。
return
是一个函数,在本例中,其类型为
return :: a -> IO a
它真的应该被称为 pure
因为它采用纯值并将其包装在单子上下文中。 (当你进入 Applicative Functors 时,你会发现 return
的一个版本叫做 pure
)。
关于 monad 的一个规则是,一旦进入 monadic 上下文,就无法退出;您可以在上下文中对值进行纯计算,但结果保留在单子上下文中。该上下文由 "IO" 之类的类型表示。所以在这种情况下你的函数类型应该是
vote :: PostId -> Bool -> IO (Maybe (Int, Int))
但是,如果您尝试这样做,您会发现它仍然不起作用。 case
的第一个分支很好,因为它 return 是 Nothing
。但是第二个分支有一个类型错误,因为它在一个 IO 动作之后试图 return (nuv, nvd)
。您已尝试分解 return
,但实际上您需要在第一个分支中 return Nothing
,然后在第二个分支末尾 return $ Just (nuv, nvd)
。
顺便说一句,您不需要为每个值都创建一个新的 let
。你可以说
let
foo = 1
bar = 2
return (foo, bar)
所以我是 Haskell 的新手,可能会问一个非常愚蠢的问题。 我尝试将以下代码获取到 运行:
asdf :: Maybe a -> Maybe a
asdf k = do
return $ case k of
Nothing -> Nothing
Just x -> Just x
我知道这基本上是 fmap
并且仅使用 return 是没有意义的。我真正想做的是以下
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)
我想这有不止一个问题。我的第一个例子到底有什么问题?第二个问题的方法是完全错误的还是怎么回事?你会怎么写?
您的第一个代码示例不需要 return
.
你知道return
在Haskell中的意思了吗?它不像任何其他编程语言中的 return
。在大多数语言中,它是一个控制流语句,return 控制带有附加值的调用函数。在 Haskell 中,它只是一个具有误导性名称的函数。
在这种情况下你应该说:
asdf k =
case k of
Nothing -> Nothing
Just x -> Just x
在 Haskell do
中,符号与 monads 一起使用,如 IO。令人困惑的是 Maybe
实际上也是一个 monad。事实上,它是最简单的 monad。只有当你想要在某种上下文中 运行 的菊花链函数时,你才需要使用 monad,而这里不是这种情况。
继续你的第二个例子,问题是 runSQL
必须与外界互动,所以几乎可以肯定 return 是某种类型的 IO a
a
。在这种情况下,您是在外部世界的上下文中执行菊花链功能,因此使用 monad 是必要的。
return
是一个函数,在本例中,其类型为
return :: a -> IO a
它真的应该被称为 pure
因为它采用纯值并将其包装在单子上下文中。 (当你进入 Applicative Functors 时,你会发现 return
的一个版本叫做 pure
)。
关于 monad 的一个规则是,一旦进入 monadic 上下文,就无法退出;您可以在上下文中对值进行纯计算,但结果保留在单子上下文中。该上下文由 "IO" 之类的类型表示。所以在这种情况下你的函数类型应该是
vote :: PostId -> Bool -> IO (Maybe (Int, Int))
但是,如果您尝试这样做,您会发现它仍然不起作用。 case
的第一个分支很好,因为它 return 是 Nothing
。但是第二个分支有一个类型错误,因为它在一个 IO 动作之后试图 return (nuv, nvd)
。您已尝试分解 return
,但实际上您需要在第一个分支中 return Nothing
,然后在第二个分支末尾 return $ Just (nuv, nvd)
。
顺便说一句,您不需要为每个值都创建一个新的 let
。你可以说
let
foo = 1
bar = 2
return (foo, bar)