-
OrPatterns -
- Since:
-
9.12.1
Разрешает использование синтаксиса шаблонов «или».
Шаблоны «или» активируются расширением языка OrPatterns.
Они позволяют объединить несколько шаблонов в один.
Предположим, у нас есть какой-то тип суммы и код, сопоставляющий его:
data Sweet = Cupcake | Liquorice | Cookie | Raisins tasty Cupcake = True tasty Cookie = True tasty _ = False
Допустим, нам нужно добавить ещё один конструктор в наш тип, например Cheesecake. Из-за используемого шаблона подстановки по умолчанию при определении tasty, компилятор не предупреждает нас о том, что сопоставление шаблонов может потребовать корректировки, что приводит к ошибочной классификации чизкейка как невкусного.
Если мы хотим, чтобы компилятор помогал нам в Haskell2010, мы должны явно записать все случаи, увеличив код в столбцах. Именно здесь помогают шаблоны «или». С помощью OrPatterns мы можем написать:
tasty (Cupcake; Cookie) = True tasty (Liquorice; Raisins) = False
Если мы расширим Sweet ещё одним конструктором, теперь мы получим предупреждение о неполном сопоставлении шаблонов — при условии компиляции с -Wincomplete-patterns.
Шаблоны «или» особенно полезны в сопоставлениях шаблонов, которые должны обрабатывать большое количество конструкторов. Не редкость встречаются сопоставления шаблонов, имеющие десятки конструкторов, например, в собственном коде GHC (Pat.hs). В таких случаях единственные варианты:
- использовать шаблон подстановки по умолчанию и, ценой ясности, рискуя ошибками при добавлении новых конструкторов
- перечислить каждый конструктор, ценой дублирования правой части кода
- использовать шаблон «или»
6.7.5.1. Спецификация
Шаблон «или» выглядит так:
(pat_1; ...; pat_n)
где pat_1, …, pat_n — это сами шаблоны. Шаблоны «или» являются обычными шаблонами и могут использоваться везде, где могут использоваться другие шаблоны.
Результат сопоставления значения x с этим шаблоном:
- результат сопоставления
xсpat_1в случае успешного сопоставления - результат сопоставления
xс(pat_2; ...; pat_n)в противном случае.
Основное ограничение шаблонов «или» на данный момент состоит в том, что они не могут связывать какие-либо переменные или ограничения. Это запрещает код вида
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`
Это связано с тем, что пока не предложена более общая статическая семантика для таких шаблонов «или».
Итак, что могут делать шаблоны «или»?
Помимо уменьшения размера кода и дублирования, они работают совместно со всеми формами существующих шаблонов, такими как шаблоны представления и синонимы шаблонов:
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
Шаблоны «или» не используют обратное отслеживание при наличии защищенных правых частей, т. е. когда один вариант шаблона «или» соответствует, другие не проверяются, если условие не выполнено. Следующий код даёт "no backtracking":
case error "backtracking" of (_; True) | False -> error "inaccessible" _ -> error "no backtracking"
(Точная синтаксическая конструкция и семантика шаблонов «или» находятся здесь.)