-
PatternSynonyms -
- Since:
-
7.8.1
Разрешают определение синонимов шаблонов.
Синонимы шаблонов активируются языковым расширением PatternSynonyms, которое необходимо для их определения, но не для их использования. Более подробную информацию и примеры синонимов шаблонов можно найти на странице Wiki.
Синонимы шаблонов позволяют давать имена параметризованным схемам шаблонов. Их также можно рассматривать как абстрактные конструкторы, не влияющие на представление данных. Например, в реализации языка программирования мы можем представлять типы языка следующим образом:
data Type = App String [Type]
Вот несколько примеров использования данного представления. Рассмотрим несколько типов из Type вселенной, закодированных таким образом:
App "->" [t1, t2] -- t1 -> t2 App "Int" [] -- Int App "Maybe" [App "Int" []] -- Maybe Int
Это представление очень общее, поскольку никаким типам не уделяется особого внимания. Однако некоторые функции могут нуждаться в специальной обработке некоторых известных типов, например, следующие две функции собирают все типы аргументов (вложенных) типов стрелок и распознают тип Int соответственно:
collectArgs :: Type -> [Type] collectArgs (App "->" [t1, t2]) = t1 : collectArgs t2 collectArgs _ = [] isInt :: Type -> Bool isInt (App "Int" []) = True isInt _ = False
Сопоставление с App напрямую сложно читать и склонно к ошибкам в написании. И ситуация становится еще хуже, когда сопоставление вложено:
isIntEndo :: Type -> Bool isIntEndo (App "->" [App "Int" [], App "Int" []]) = True isIntEndo _ = False
Синонимы шаблонов позволяют абстрагироваться от представления, чтобы раскрыть сопоставители, которые ведут себя как конструкторы в отношении сопоставления с шаблоном. Мы можем создать синонимы шаблонов для известных типов, которые нас интересуют, не привязывая к ним представление (обратите внимание, что их не обязательно определять в том же модуле, что и тип Type):
pattern Arrow t1 t2 = App "->" [t1, t2] pattern Int = App "Int" [] pattern Maybe t = App "Maybe" [t]
Что позволяет нам переписать наши функции в гораздо более чистом стиле:
collectArgs :: Type -> [Type] collectArgs (Arrow t1 t2) = t1 : collectArgs t2 collectArgs _ = [] isInt :: Type -> Bool isInt Int = True isInt _ = False isIntEndo :: Type -> Bool isIntEndo (Arrow Int Int) = True isIntEndo _ = False
В целом, существует три вида синонимов шаблонов. Однонаправленные, двунаправленные и явно двунаправленные. Приведенные выше примеры являются примерами двунаправленных синонимов шаблонов. Двунаправленный синоним ведет себя так же, как обычный конструктор данных. Мы можем использовать его в контексте шаблона для деконструкции значений и в контексте выражения для построения значений. Например, мы можем построить значение intEndo с помощью синонимов шаблонов Arrow и Int , определённых ранее.
intEndo :: Type intEndo = Arrow Int Int
Этот пример эквивалентен гораздо более сложному построению, если бы мы напрямую использовали конструкторы Type.
intEndo :: Type intEndo = App "->" [App "Int" [], App "Int" []]
Однонаправленные синонимы могут использоваться только в контексте шаблона и определяются следующим образом:
pattern Head x <- x:xs
В этом случае, Head ⟨x⟩ нельзя использовать в выражениях, только в шаблонах, поскольку это не определит значение для ⟨xs⟩ в правой части. Однако мы можем определить явно двунаправленный синоним шаблона, отдельно указав, как построить и деконструировать тип. Синтаксис для этого следующий:
pattern HeadC x <- x:xs where HeadC x = [x]
Тогда мы можем использовать HeadC как в контексте выражения, так и в контексте шаблона. В контексте шаблона он будет сопоставляться с заголовком любого списка длиной не менее одного. В контексте выражения он будет строить список-одиночку.
Явно двунаправленные синонимы шаблонов предлагают большую гибкость, чем неявно двунаправленные, в терминах разрешенного синтаксиса. Например, следующее не является допустимым неявно двунаправленным синонимом шаблона:
pattern StrictJust a = Just !a
Это недопустимо, поскольку использование BangPatterns в правой части предотвращает формирование корректного выражения. Однако создание строгого синонима шаблона вполне возможно с помощью явно двунаправленного синонима шаблона:
pattern StrictJust a <- Just !a where StrictJust !a = Just a
Создание явно двунаправленного синонима шаблона также:
- может создавать разные конструкторы данных из базового типа данных, а не только тот, который появляется в сопоставлении с шаблоном;
- может вызывать любые функции или условную логику, особенно валидацию, при условии, что она создает результат нужного типа;
- может использовать условия в левой части
=; - может иметь несколько уравнений.
Например:
data PosNeg = Pos Int | Neg Int
pattern Smarter{ nonneg } <- Pos nonneg where
Smarter x = if x >= 0 then (Pos x) else (Neg x)
Или с использованием условий:
pattern Smarter{ nonneg } <- Pos nonneg where
Smarter x | x >= 0 = (Pos x)
| otherwise = (Neg x)
Существует обширное искусство хаскельских народных ремесел умных конструкторов, по сути, функций, которые добавляют валидацию вокруг конструктора и скрывают его представление. Недостатком является то, что базовый конструктор не может использоваться как сопоставитель. Синонимы шаблонов могут использоваться как подлинные умные конструкторы, как для валидации, так и для сопоставления.
В таблице ниже обобщается, где можно использовать каждый вид синонима шаблона.
Контекст | Однонаправленный | Двунаправленный | Явно двунаправленный |
|---|---|---|---|
Шаблон | Да | Да | Да |
Выражение | Нет | Да (Вывод) | Да (Явно) |
6.7.4.1. Синонимы шаблонов записей
Также возможно определить синонимы шаблонов, которые ведут себя как конструкторы записей. Синтаксис для этого следующий:
pattern Point :: Int -> Int -> (Int, Int)
pattern Point{x, y} = (x, y)
Идея состоит в том, что мы можем использовать Point так, как будто мы определили новый тип данных MyPoint с двумя полями x и y.
data MyPoint = Point { x :: Int, y :: Int }
В то время как обычный синоним шаблона можно использовать двумя способами, есть семь способов использования Point. Точно так же, как и обычный конструктор записей.
Использование | Пример |
|---|---|
В качестве конструктора |
|
В качестве конструктора с синтаксисом записей |
|
В контексте шаблона |
|
В контексте шаблона с синтаксисом записей |
|
В контексте шаблона с именованием полей |
|
В обновлении записи |
|
Использование селекторов записей |
|
Для однонаправленного синонима шаблона записи мы определяем селекторы записей, но не разрешаем обновления записей или построение.
Синтаксис и семантика синонимов шаблонов подробно описаны в следующих подразделах. Более подробная информация также представлена на странице статьи.
Дополнительные сведения см. на странице Wiki.
6.7.4.2. Синтаксис и область действия синонимов шаблонов
Декларация синонима шаблона может быть однонаправленной, двунаправленной или явно двунаправленной. Синтаксис однонаправленных синонимов шаблонов:
pattern pat_lhs <- pat
синтаксис двунаправленных синонимов шаблонов:
pattern pat_lhs = pat
и синтаксис явно двунаправленных синонимов шаблонов:
pattern pat_lhs <- pat where pat_lhs = expr -- lhs restricted, see below
Мы можем определить префиксные, инфиксные или синонимы шаблонов записей, изменив форму pat_lhs. Синтаксис для них следующий:
Префиксный |
|
Инфиксный |
|
Записи |
|
pat_lhs для явно двунаправленного построения не может использовать синтаксис записей. (Потому что правая часть expr может строить различные конструкторы данных.) Она может использовать условия с несколькими уравнениями.
Декларации синонимов шаблонов могут появляться только на верхнем уровне модуля. В частности, они не допускаются в качестве локальных определений.
Переменные в левой части определения связываются шаблоном в правой части. Для двунаправленных синонимов шаблонов все переменные в правой части также должны присутствовать в левой части; также не допускаются шаблоны с подстановкой и шаблоны представлений. Для однонаправленных и явно двунаправленных синонимов шаблонов нет ограничений на шаблон в правой части.
Синонимы шаблонов не могут быть определены рекурсивно.
ПРАГМЫ COMPLETE могут быть указаны, чтобы указать проверяющему исчерпываемость сопоставления с шаблоном, что набор синонимов шаблонов является полным.