Вот три языка, которые позволяют вам определять своих собственных операторов, которые делают две с половиной разные вещи! Haskell и Coq оба запрещают эти виды махинаций - но по-разному - в то время как Agda допускает такое смешение ассоциаций.
Во-первых, в Haskell вам просто не разрешено это делать. Вы можете определить свои собственные операторы и дать им приоритет (от 0 до 9) и ассоциативность по вашему выбору. Тем не менее, отчет Haskell запрещает вам смешивать ассоциации :
Последовательные операторы без скобок с одинаковым приоритетом должны быть либо левыми, либо правыми, чтобы избежать синтаксической ошибки. [Haskell 2010 Report, Ch. 3]
Таким образом, в GHC , если мы определим левоассоциативныйinfixl
оператор ( ) <@
и правоассоциативный оператор @>
на одном и том же уровне приоритета - скажем, 0 - тогда вычисление x <@ y @> z
даст ошибку
Ошибка разбора приоритета
не может смешивать ' <@
' [ infixl 0
] и ' @>
' [ infixr 0
] в одном и том же инфиксном выражении
(На самом деле, вы также можете объявить оператор инфиксным, но неассоциативным, например ==
, так что x == y == z
это синтаксическая ошибка!)
С другой стороны, есть агда-зависимый провайдер языка / теорем Agda (который, по общему признанию, является значительно менее распространенным). Agda имеет один из самых податливых синтаксисов из всех известных мне языков, поддерживающих операторы mixfix : стандартная библиотека содержит функцию
if_then_else_ : ∀ {a} {A : Set a} → Bool → A → A → A
который при вызове написан
if b then t else f
с аргументами, заполняющими подчеркивание! Я упоминаю об этом, потому что это означает, что он должен поддерживать невероятно гибкий анализ. Естественно, Agda также имеет декларации фиксированности (хотя ее уровни приоритета варьируются по произвольным натуральным числам и обычно находятся в диапазоне от 0 до 100), и Agda действительно позволяет смешивать операторы с одинаковым приоритетом, но с разными фиксированностями. Однако я не могу найти информацию об этом в документации, поэтому мне пришлось экспериментировать.
Давайте использовать наши <@
и @>
сверху. В двух простых случаях мы имеем
x <@ y @> z
разбор как x <@ (y @> z)
; и
x @> y <@ z
разбор как (x @> y) <@ z
.
Я думаю, что Агда группирует строку в «левый ассоциативный» и «правый ассоциативный» чанки, и - если я не думаю о неправильных вещах - правый ассоциативный чанк получает «приоритет» при захвате смежных аргументов. Так что это дает нам
a <@ b <@ c @> d @> e @> f <@ g
разбор как
(((a <@ b) <@ (c @> (d @> (e @> f)))) <@ g
или же
Однако, несмотря на мои эксперименты, я догадался неправильно, когда впервые написал это, что может быть поучительно :-)
(А у Agda, как и у Haskell, есть неассоциативные операторы, которые правильно выдают ошибки разбора, поэтому смешанные ассоциации могут также привести к ошибке разбора.)
Наконец, существует язык Coq для проверки теорем / зависимо-типизированного типа , который имеет даже более гибкий синтаксис, чем Agda, потому что его синтаксические расширения фактически реализованы путем предоставления спецификаций для новых синтаксических конструкций и затем переписывания их в основной язык (смутно похожий на макрос , Я предполагаю). В Coq синтаксис списка [1; 2; 3]
- это необязательный импорт из стандартной библиотеки. Новые синтаксисы могут даже связывать переменные!
Еще раз, в Coq, мы можем определить наши собственные инфиксные операторы и дать им уровни приоритета (в основном от 0 до 99) и ассоциативности. Однако в Coq каждый уровень приоритета может иметь только одну ассоциативность . Поэтому, если мы определим <@
как левоассоциативную, а затем попытаемся определить @>
как ассоциативную справа на том же уровне - скажем, 50 - мы получим
Ошибка: 50-й уровень уже объявлен левоассоциативным, а теперь ожидается, что он ассоциирован справа
Большинство операторов в Coq находятся на уровнях, которые делятся на 10; если у меня были проблемы ассоциативности (эти ассоциации уровня глобальны), я обычно просто увеличивал уровень на единицу в любом направлении (обычно вверх).