-
OrPatterns -
- Начиная с:
-
9.12.1
Разрешает использовать синтаксис OR-шаблонов.
OR-шаблоны включаются расширением языка OrPatterns.
Они позволяют объединить несколько шаблонов в один.
Предположим, у нас есть некоторый тип-сумма и код, сопоставляющий с ним образец:
data Sweet = Cupcake | Liquorice | Cookie | Raisins tasty Cupcake = True tasty Cookie = True tasty _ = False
Допустим, нам нужно добавить к нашему типу ещё один конструктор, например Cheesecake. Из-за шаблона-универсального случая, который мы использовали при определении tasty, компилятор не предупреждает нас о том, что сопоставление с образцом может потребовать изменений. В результате чизкейк ошибочно характеризуется как невкусный.
Если мы хотим, чтобы компилятор помогал нам в Haskell2010, нам пришлось бы явно перечислить все случаи, из-за чего код стал бы значительно длиннее. Здесь и помогают OR-шаблоны. С помощью OrPatterns мы можем написать:
tasty (Cupcake; Cookie) = True tasty (Liquorice; Raisins) = False
Если мы добавим к Sweet ещё один конструктор, теперь мы получим предупреждение о неполном сопоставлении с образцом — при условии, что компилируем с флагом -Wincomplete-patterns.
OR-шаблоны особенно полезны при сопоставлении с образцом, когда нужно обработать большое количество конструкторов. Нередко встречаются сопоставления с образцом, охватывающие десятки конструкторов, например, в исходном коде самого GHC (Pat.hs). В таких случаях есть только следующие варианты:
- использовать шаблон-универсальный случай, пожертвовав ясностью и рискуя допустить ошибки при добавлении новых конструкторов
- перечислить каждый конструктор, пожертвовав компактностью из-за дублирования кода правой части выражения
- использовать OR-шаблон
6.7.5.1. Спецификация
OR-шаблон выглядит так:
(pat_1; ...; pat_n)
где pat_1, …, pat_n — это шаблоны. OR-шаблоны являются обычными шаблонами и могут использоваться везде, где применимы другие шаблоны.
Результат сопоставления значения x с этим шаблоном:
- результат сопоставления
xсpat_1, если оно не завершилось неудачей - в противном случае — результат сопоставления
xс(pat_2; ...; pat_n).
Основное ограничение OR-шаблонов на данный момент состоит в том, что они не могут связывать переменные или ограничения. Это запрещает такой код:
value :: Either a a -> a value (Left x; Right x) = x -- binds a variable
или такой:
data G a where G1 :: Num a => G a G2 :: Num a => G a bar :: G a -> a bar (G1; G2) = 3 -- cannot solve constraint `Num a` data GADT a where IsInt1 :: GADT Int IsInt2 :: GADT Int foo :: a -> GADT a -> a foo x (IsInt1; IsInt2) = x + 1 -- cannot solve constraint `Num a`
Это ограничение существует лишь потому, что для таких OR-шаблонов пока не предложена более общая статическая семантика.
Итак, что же могут делать OR-шаблоны?
Помимо сокращения размера кода и устранения дублирования, они сочетаются со всеми существующими видами шаблонов, например с шаблонами просмотра и синонимами шаблонов:
f :: (Eq a, Show a) => a -> a -> Bool f a ((== a) -> True; show -> "yes") = True f _ _ = False small (abs -> (0; 1; 2); 3) = True -- -3 is not small small _ = False type Coll a = Either [a] (Set a) pattern None <- (Left []; Right (toList -> [])) empty None = False empty _ = True
OR-шаблоны не выполняют возврат при использовании охраняемых правых частей: если совпало одно из альтернативных условий OR-шаблона, остальные не проверяются, даже если проверка охраны завершается неудачей. Следующий код выдаёт "no backtracking":
case error "backtracking" of (_; True) | False -> error "inaccessible" _ -> error "no backtracking"
(Точный синтаксис и семантику OR-шаблонов можно найти здесь.)