Oto trzy języki, które pozwalają zdefiniować własnych operatorów, którzy robią dwie i pół różne rzeczy! Haskell i Coq nie pozwalają na tego rodzaju shenanigany - ale inaczej - podczas gdy Agda pozwala na takie mieszanie skojarzeń.
Po pierwsze, w Haskell po prostu nie wolno ci tego robić. Możesz zdefiniować własnych operatorów i nadać im pierwszeństwo (od 0–9) i wybrane przez siebie skojarzenie. Jednak raport Haskella zabrania mieszania skojarzeń :
Kolejne niepodzielne operatory o tym samym priorytecie muszą być albo lewe, albo prawe asocjacyjne, aby uniknąć błędu składniowego. [Raport Haskell 2010, rozdz. 3]
Tak więc w GHC , jeśli zdefiniujemy infixl
operator lewostronny ( ) <@
i prawostronny @>
na tym samym poziomie pierwszeństwa - powiedzmy 0 - wtedy ocena x <@ y @> z
daje błąd
Błąd analizy pierwszeństwa
nie może łączyć „ <@
” infixl 0
] i @>
„[ infixr 0
] w tym samym wyrażeniu infix
(W rzeczywistości można również zadeklarować, że operator jest nieoznaczony, ale nie jest skojarzony, na przykład ==
, więc x == y == z
jest to błąd składniowy!)
Z drugiej strony istnieje zależna od języka pisarka / twierdzarka Agda (która, co prawda, jest znacznie mniej popularna). Agda ma jedne z najbardziej plastycznych składni w dowolnym języku, który znam, obsługując operatory mixfix : standardowa biblioteka zawiera funkcję
if_then_else_ : ∀ {a} {A : Set a} → Bool → A → A → A
który po wywołaniu jest napisany
if b then t else f
z argumentami wypełniającymi podkreślenia! Wspominam o tym, ponieważ oznacza to, że musi obsługiwać niezwykle elastyczną analizę. Naturalnie Agda ma również deklaracje stałość (choć jej poziom pierwszeństwa wynosić ponad dowolnych liczb naturalnych i są zwykle w 0-100), a Agda nie pozwalają mieszać operatorów o tym samym priorytecie, ale różnych fixities. Nie mogę jednak znaleźć informacji na ten temat w dokumentacji, więc musiałem eksperymentować.
Użyjmy ponownie naszego <@
i @>
z góry. W dwóch prostych przypadkach mamy
x <@ y @> z
parsowanie jako x <@ (y @> z)
; i
x @> y <@ z
parsowanie jako (x @> y) <@ z
.
Myślę, że to , co robi Agda, to pogrupowanie wiersza na fragmenty „lewostronnie asocjacyjne” i „prawostronne” i - chyba że myślę o czymś złym - fragment prawostronny ma „priorytet” w przejmowaniu sąsiednich argumentów. To nam daje
a <@ b <@ c @> d @> e @> f <@ g
parsowanie jako
(((a <@ b) <@ (c @> (d @> (e @> f)))) <@ g
lub
Jednak pomimo moich eksperymentów pomyliłem się, kiedy napisałem to po raz pierwszy, co może być pouczające :-)
(A Agda, podobnie jak Haskell, ma niepowiązane operatory, które poprawnie podają błędy analizy, więc mieszane skojarzenia mogłyby również spowodować błąd analizy).
Wreszcie istnieje Coq -prover / zależnie typowany język , który ma jeszcze bardziej elastyczną składnię niż Agda, ponieważ jego rozszerzenia składniowe są w rzeczywistości implementowane poprzez podanie specyfikacji nowych konstrukcji składniowych, a następnie przepisanie ich na język podstawowy (niejasno podobny do makr , Przypuszczam). W Coq składnia listy [1; 2; 3]
jest opcjonalnym importem ze standardowej biblioteki. Nowe składnie mogą nawet wiązać zmienne!
Po raz kolejny w Coq możemy zdefiniować własnych operatorów infix i nadać im poziomy pierwszeństwa (głównie od 0 do 99) oraz powiązania. Jednak w Coq każdy poziom pierwszeństwa może mieć tylko jedno skojarzenie . Więc jeśli zdefiniujemy <@
jako lewostronny, a następnie spróbujemy zdefiniować @>
jako prawostronny na tym samym poziomie - powiedzmy 50 - otrzymamy
Błąd: Poziom 50 jest już zadeklarowany jako lewostronny, podczas gdy oczekuje się, że będzie prawostronny
Większość operatorów w Coq znajduje się na poziomach, które można podzielić przez 10; jeśli miałem problemy z asocjatywnością (te poziomy są globalne), generalnie po prostu podniosłem poziom o jeden w dowolnym kierunku (zwykle w górę).