Почему активные шаблоны требуют специального синтаксиса? - PullRequest
1 голос
/ 14 ноября 2011

Если бы обычные функции могли использоваться в качестве шаблонов, это избавило бы от необходимости писать тривиальные активные шаблоны, такие как

let (|NotEmpty|_|) s = Seq.tryPick Some s

и, гипотетически, разрешит

let s = seq []
match s with
| Seq.tryPick Some -> ...
| _ -> //empty

Это сделало бы функции более пригодными для повторного использования, избавив от необходимости «шаблонизировать» функции, которые вы хотите использовать с соответствием:

let f x = if x then Some() else None
let (|F|_|) = f

Я знаю, что активные шаблоны могут быть вызваны как функции, поэтому предыдущий пример можно упростить, определив шаблон only . Но отказ от специального синтаксиса шаблонов упрощает это.

Каковы причины специального синтаксиса?

EDIT

В дальнейшем активный шаблон затеняет литерал.

[<Literal>]
let X = 1
let (|X|_|) x = if x = 0 then Some() else None

match 0 with //returns true
| X -> true
| _ -> false

Почему это не работает и для вызовов функций в шаблонах?

РЕДАКТИРОВАТЬ 2

Я нашел сценарий, который был бы неоднозначным

let zero n = if n = 0 then Some() else None
match 0 with
| zero -> //function call or capture?

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

Ответы [ 2 ]

5 голосов
/ 15 ноября 2011

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

Например, предположим, что мы используем тип Info для представления некоторой информации:

type Info = I of string * int

Вы можете написать две функции для создания и уничтожения значений этого типа:

val constructInfo : string * int -> Info
val destructInfo  : Info -> string * int

В этом случае реализовать две функции тривиально, но интересным моментом является то, что сигнатуры их типов являются двойственными. Конструкция принимает значения и создает наш (абстрактный) тип, а разрушение - тип и возвращает отдельные значения.

Используя активные шаблоны, мы можем использовать одно и то же имя, Info, для обеих целей. Это делает его совместимым с остальным языком - например, x::xs и (a, b) являются как конструкторами, так и шаблонами. Библиотека F # делает то же самое для цитат F # (т. Е. Lambda является шаблоном и конструктором для типа Expr.

Таким образом, вместо написания конструкций и уничтожения функций, мы можем определить функцию и шаблон:

let Info (a, b) = I (a, b)
let (|Info|) (I (a, b)) = (a, b)

В результате один и тот же синтаксис, Info(a, b), может отображаться как шаблон и выражение.

1 голос
/ 14 ноября 2011

Шаблоны и переменные с привязкой по буквам имеют разные пространства имен, что имеет смысл в большинстве случаев, учитывая, как часто происходит теневое копирование и как часто используются короткие идентификаторы. Например, вы можете определить x в первой строке вашей программы, а затем 200 строк будут иметь match ... with | (x,y) -> x + y, и в этом случае вы почти наверняка захотите, чтобы x был новым идентификатором.

Если вы хотите использовать произвольные функции, просто используйте параметризованный активный шаблон:

let (|Id|_|) f x = f x

match seq [] with
| Id (Seq.tryPick Some) _ -> ...

EDIT

См. Разрешение имени для шаблонов в спецификации для получения подробной информации о разрешении имени. Ключ в том, что существует логическая таблица PatItems , которая отличается от таблицы ExprItems , которая используется для имен в выражениях. В конкретном случае, который вы добавили к редактированию в своем вопросе, выигрывает последнее определение X, поэтому в этом случае оно рассматривается как активный шаблон (эффективно затеняет литерал, когда в шаблоне появляется X).

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

...