Подтвердить что ты не робот

Or-patterns в Haskell

В OCaml я использовал для написания кода, который выглядел так:

let combine o1 o2 = match o1, o2 with
    | Valid, Invalid | Invalid, Valid -> Invalid
    | _ -> ...

Я не нашел способ написать или-шаблоны в Haskell, и я действительно скучаю по нему. У кого-нибудь есть решение?

4b9b3361

Ответ 1

Я не думаю, что это возможно в haskell. Однако есть несколько альтернатив:

Выделить общий код с привязкой where

Это не имеет особого смысла в вашем примере, но полезно, если у вас больше кода в выражении case:

combine o1 o2 = case (o1,o2) of
  (Valid, Invalid) -> handleInvalid
  (Invalid, Valid) -> handleInvalid
  ...
 where
  handleInvalid = ...

Использование шаблонов подстановок

По моему опыту, это происходит не так часто, что вы хотите использовать два или шаблоны в одном шаблоне. В этом случае вы можете обрабатывать все "хорошие" случаи и использовать шаблон для всех остальных:

combine o1 o2 = case (o1,o2) of
  (Valid, Valid) -> Valid -- This is the only valid case
  _              -> Invalid -- All other cases are invalid

Это имеет тот недостаток, что он обходит проверку целостности и что вы не можете использовать шаблоны подстановок для других целей.

Использовать защитные устройства и ==

Если типы, которые вы хотите сопоставить, являются типами, подобными перечислению, вы можете подумать о создании экземпляра Eq. Затем вы можете использовать == и || для соответствия нескольким конструкторам в одном защитнике:

combine o1 o2
  | o1 == Invalid && o2 == Valid || o1 == Valid && o2 == Invalid = Invalid
  | ...

Я согласен с тем, что это выглядит не так хорошо, и у него также есть недостаток в обход проверки целостности и не предупреждает вас, если шаблоны перекрываются, поэтому я бы не рекомендовал его.

Ответ 2

Вы можете использовать этот квазикватер http://hackage.haskell.org/package/OrPatterns. Ваш пример переводится как-то вроде:

let combine o1 o2 = case (o1, o2) of
      [o| (Valid, Invalid) | (Invalid, Valid ) |] -> Invalid
      _ -> ...

Ответ 3

Существует proposal для добавления or-patterns в GHC.

До тех пор (в дополнение к другим примерам) синонимы шаблонов могут использоваться для аналогичных средств:

data ABC = A Int | B Int | C Bool

ab :: ABC -> Maybe Int
ab (A i) = Just i
ab (B i) = Just i
ab C{}   = Nothing

pattern AB :: Int -> ABC
pattern AB i <- (ab -> Just i)

Если вы хотите сопоставить значения отдельных типов (например, Int и Bool), вы можете создать некоторый ограниченный экзистенциальный тип

data Showable where
  Showable :: Show a => a -> Showable

ac :: ABC -> Maybe Showable
ac (A i) = Just (Showable i)
ac (C b) = Just (Showable b)
ac B{}   = Nothing

pattern AC :: () => Show a => a -> ABC
pattern AC showable <- (ac -> Just (Showable showable))

showAC :: ABC -> String
showAC (AC showable) = "A or C: " ++ show showable
showAC (B i)         = "B: "      ++ show i