如何组合 case 语句模式

How to combine case statement patterns

我试图在 case 语句中匹配许多不同的构造函数。为简单起见,假设在一半情况下我们做同样的事情,而在另一半情况下我们做其他事情。即使我把逻辑分解到另一个函数,我仍然要写:

case x of
  C1 -> foo x
  C2 -> foo x
  ...
  C10 -> bar x
  C11 -> bar x
  ...

有什么方法可以使 case 语句的行为更像 C 中的 switch 语句(即 fallthrough),或者这样我就可以同时匹配多种模式之一,例如:

case x of
  C1, C2, C3 -> foo x
  C10, C11, C12 -> bar x

或者另一种清理方法?

这些称为 析取模式,Haskell 没有。 (OCaml 和 F# 可以。)但是,有一些典型的解决方法。如果您的类型是枚举,则可以使用相等性,例如 elem,使用 case 表达式、守卫或 MultiWayIf:

exampleCase cond = case cond of
  c
    | c `elem` [C1, C2, C3] -> foo
    | c `elem` [C10, C11, C12] -> bar
    | otherwise -> baz
exampleGuards c
  | c `elem` [C1, C2, C3] -> foo
  | c `elem` [C10, C11, C12] -> bar
  | otherwise -> baz
exampleIf c
  = additionalProcessing $ if
    | c `elem` [C1, C2, C3] -> foo
    | c `elem` [C10, C11, C12] -> bar
    | otherwise -> baz

当然,如果 foobar 是长表达式,由于懒惰,您可以简单地将它们分解为本地定义,因此您只需要重复名称和任何模式变量需要作为参数:

exampleWhere cond = case cond of
  C1 x -> foo x
  C2 y -> foo y
  …
  C10 -> bar
  C11 -> bar
  …
  where
    foo x = something long (involving x, presumably)
    bar = if you please then something else quite long

如果您经常以这种方式将构造函数组合在一起,您可以使用 PatternSynonyms 语言选项,它与 ViewPatterns 结合使用特别有用,可以创建您自己的匹配此类组的模式:

{-# Language
    LambdaCase,
    PatternSynonyms,
    ViewPatterns #-}

-- Write one function to match each property.

fooish :: T -> Maybe X
fooish = \ case
  C1 x -> Just x
  C2 x -> Just x
  …
  C10 -> Nothing
  C11 -> Nothing
  …
  -- May use a wildcard ‘_’ here; I prefer not to,
  -- to require updating cases when a type changes.

barrish :: T -> Bool
barrish = \ case
  C1{} -> False
  C2{} -> False
  …
  C10 -> True
  C11 -> True
  …

-- Create synonyms for matching those properties.
-- (These happen to be unidirectional only.)

pattern Fooish :: T -> Foo
pattern Fooish x <- (fooish -> Just x)

pattern Barrish :: T -> Bar
pattern Barrish <- (barrish -> True)

-- If they cover all cases, tell the compiler so.
-- This helps produce useful warnings with ‘-Wall’.

{-# Complete Fooish, Barrish #-}

-- Use them just like normal patterns.

exampleSynonyms x = case x of
  Fooish x -> …
  …
  Barrish -> …
  …