Ось три мови, які дозволяють вам визначити власних операторів, які роблять дві з половиною різні речі! Хаскелл і Кок забороняють подібні шнанігани - але по-різному - в той час як Агда дозволяє подібне змішування асоціативностей.
По-перше, у Haskell вам просто не дозволено цього робити. Ви можете визначити власні оператори та надати їм перевагу (від 0 до 9) та асоціативність на ваш вибір. Однак звіт Haskell забороняє вас змішувати асоціативи :
Послідовні неопечатані оператори з однаковим пріоритетом повинні бути або лівими, або правими асоціативними, щоб уникнути помилки синтаксису. [Звіт Haskell 2010, гол. 3]
Так, у GHC , якщо ми визначаємо ліво-асоціативний ( infixl
) оператор <@
та правоасоціативний оператор @>
на одному рівні пріоритетності - скажімо, 0 - тоді оцінювання x <@ y @> z
дає помилку
Помилка розбору прецеденсу
не може змішувати ' <@
' [ infixl 0
] і ' @>
' [ infixr 0
] в одному виразі інфіксації
(Насправді ви також можете оголосити оператора інфіксом, але неасоціативним, як ==
, отже, x == y == z
це синтаксична помилка!)
З іншого боку, є Agda (що, правда, значно менше мейнстріму). У Agda є один із найменш синтаксисних синтаксисів будь-якої моєї мови, що підтримує оператори змішування : стандартна бібліотека містить функцію
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; якщо у мене виникли проблеми з асоціативністю (ці асоціації рівня є глобальними), я взагалі просто натрапив на рівень в будь-якому напрямку (як правило, вгору).