haskell 是否有与 rust 的 if let pattern = mightMatch 语法等效的语法?

thi*_*ndy 9 haskell

在 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 的答案详细扩展了正确的思维方式。

Jos*_*ica 9

当您想要匹配特定的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每个都包含零个(NothingLeft)或一个(JustRight)这些值。traverse_接受 a Foldable,将提供的函数应用于它的每个值以获取上下文,然后应用所有上下文。对于零值,它不会执行任何操作,只是返回pure (),而对于一个值,它会在一个值上运行该函数。


Wil*_*sem 8

我不明白为什么你必须提到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 ()行为相当于无操作。有时这被命名为skipyield

  • 呵呵,这并不重要,但是 `forM_` 需要一个 `Monad`,尽管它替换的代码只需要一个 `Applicative`。我刚刚打开 https://github.com/ndmitchell/hlint/pull/1437 来建议“for_”。 (5认同)
  • 有趣的是,[hlint](https://github.com/ndmitchell/hlint) 建议“为什么不使用 `forM_ mx leftover`” 来代替这个答案的表述,这正是 @JosephSible-ReinstateMonica 的答案。 (3认同)