在 Rust 中,我们可以说例如
if Some(inner_val) = option { ... }
Run Code Online (Sandbox Code Playgroud)
Haskell 有等效的吗?
我将提供一些背景信息,以防这看起来像是一件愚蠢的事情(请随意告诉我这是一件愚蠢的事情)。我一边阅读管道自述文件一边做练习。其中之一指示读者“实现一个查看函数,从上游获取下一个值(如果可用),然后将其放回流中。” 我这样做如下:
peek :: Monad m => ConduitT a o m (Maybe a)
peek = do
mx <- await
case mx of
Nothing -> return mx
Just x -> do
leftover x
return mx
Run Code Online (Sandbox Code Playgroud)
我不喜欢我必须return mx用双臂说这句话。人们很容易想象这样的情况:手臂之间分担的工作比简单的工作要多得多return。我想要一种方法来有条件地完成不共享的工作,然后继续前进。我可以说
peek2 :: Monad m => ConduitT a o m (Maybe a)
peek2 = do
mx <- await
when
(isJust mx)
( do
let Just x = mx
leftover x
)
return mx
Run Code Online (Sandbox Code Playgroud)
但这匹配了mx两次,更不用说编译器对我生气了。此外,它特定于Maybe,但我可能想匹配任意类型,并且仅在它是特定形状时才执行某些操作。执行此操作的惯用方法是什么?
编辑:也许值得在问题文本中提到,使用 case 表达式不会将后续计算“困在”它的怀抱中,正如我在写这篇文章时似乎错误地相信的那样。@Willem-Van-Onsem 的答案简洁地解决了这个问题,@Ben 的答案详细扩展了正确的思维方式。
当您想要匹配特定的Maybe存在Just(或其他一些情况,例如存在Either)时Right,正如您在示例中所做的那样,您可以使用traverse_:
import Data.Conduit
import Data.Foldable (traverse_)
peek :: Monad m => ConduitT a o m (Maybe a)
peek = do
mx <- await
traverse_ leftover mx
return mx
Run Code Online (Sandbox Code Playgroud)
如果您想要在内部调用多个函数,您可以使用for_(相当于flip traverse_) 来代替,并在方便的 lambda 中写出更多步骤,如下所示:
import Data.Conduit
import Data.Foldable (for_)
peek :: Monad m => ConduitT a o m (Maybe a)
peek = do
mx <- await
for_ mx $ \x ->
foo x
bar x
baz x
return mx
Run Code Online (Sandbox Code Playgroud)
为什么这样有效:AFoldable包含一定数量的其类型的值,可以提取这些值然后对其进行操作。Maybe并且Either每个都包含零个(Nothing或Left)或一个(Just或Right)这些值。traverse_接受 a Foldable,将提供的函数应用于它的每个值以获取上下文,然后应用所有上下文。对于零值,它不会执行任何操作,只是返回pure (),而对于一个值,它会在一个值上运行该函数。
我不明白为什么你必须提到return mx两次。这相当于:
peek :: Monad m => ConduitT a o m (Maybe a)
peek = do
mx <- await
case mx of
Just x -> leftover x
_ -> pure ()
return mx
Run Code Online (Sandbox Code Playgroud)
因此,如果您愿意,它return mx是“外部do块”的一部分。该pure ()行为相当于无操作。有时这被命名为skip或yield。