Termin „ortogonalność” jest terminem laickim dla precyzyjnego pojęcia matematycznego: terminy językowe tworzą początkową algebrę (patrz na Wikipedii).
Oznacza to w zasadzie „istnieje zgodność 1-1 między składnią a znaczeniem”. Oznacza to: istnieje dokładnie jeden sposób wyrażania rzeczy, a jeśli umieścisz jakieś wyrażenie w określonym miejscu, możesz także umieścić w nim inne wyrażenie.
Innym sposobem myślenia o „ortogonalnym” jest to, że składnia jest zgodna z zasadą podstawiania. Na przykład, jeśli masz instrukcję ze szczeliną dla wyrażenia, możesz tam umieścić dowolne wyrażenie, a wynikiem jest nadal poprawny składniowo program. Ponadto, jeśli wymienisz
Chcę podkreślić, że „znaczenie” nie oznacza wyniku obliczeniowego. Oczywiste jest, że oba 1 + 2 i 2 + 1 są równe 3. Jednak warunki są różne i implikują inne obliczenia, nawet jeśli mają ten sam wynik. Znaczenie jest inne, podobnie jak dwa algorytmy sortowania są różne.
Być może słyszałeś o „abstrakcyjnym drzewie składni” (AST). Słowo „streszczenie” oznacza tutaj dokładnie „ortogonalny”. Technicznie większość AST nie jest w rzeczywistości abstrakcyjna!
Być może słyszałeś o języku programowania „C”? Notacja typu C nie jest abstrakcyjna. Rozważać:
int f(int);
Oto zwracany typ deklaracji funkcji int
. Typ wskaźnika do tej funkcji określa:
int (*)(int)
Uwaga: nie można zapisać typu funkcji! Notacja typu C jest do bani! To nie jest abstrakcyjne. To nie jest ortogonalne. Załóżmy teraz, że chcemy stworzyć funkcję, która akceptuje powyższy typ zamiast int:
int (*) ( int (*)(int) )
Wszystko ok .. ale .. co jeśli chcemy zamiast tego zwrócić:
int (*)(int) (*) (int)
Woops! Nieważny. Pozwala dodać parens:
(int (*)(int)) (*) (int)
Woops! To też nie działa. Musimy to zrobić (to jedyny sposób!):
typedef int (intintfunc*) (int);
intintfunc (*)(int)
Teraz jest w porządku, ale użycie tu pisma maszynowego jest złe. C jest do bani. To nie jest abstrakcyjne. To nie jest ortogonalne. Oto jak to zrobić w ML, czyli:
int -> (int -> int)
Potępiamy C na poziomie składni.
Ok, teraz pozwala flogować C ++. Możemy naprawić powyższą głupotę za pomocą szablonów i uzyskać zapis podobny do ML (mniej więcej):
fun<int, int>
fun< fun<int,int>, int>
ale rzeczywisty system typów jest zasadniczo wadliwy w odniesieniu do referencji: jeśli T
jest typem, to czy jest T&
typem? Odpowiedź brzmi głupio: na poziomie składni, jeśli masz typ U = T &, wtedy U & jest dozwolone, ale oznacza to tylko, że T &: odwołanie do odwołania jest oryginalnym odwołaniem. To jest do bani! Semantycznie łamie wymóg wyjątkowości. Gorzej: T&& nie jest dozwolone składniowo: łamie to zasadę podstawiania. Tak więc odwołania do C ++ łamią ortogonalność na dwa różne sposoby, w zależności od czasu wiązania (parsowanie lub analiza typu). Jeśli chcesz zrozumieć, jak to zrobić dobrze ... nie ma problemu ze wskaźnikami!
Prawie żaden prawdziwy język nie jest ortogonalny. Nawet Schemat, który udaje wielką klarowność wypowiedzi, nie jest. Jednak wiele dobrych języków można uznać za mających „względnie bliskie podstawy cech ortogonalnych” i jest to dobra rekomendacja dla języka, zastosowanego zarówno do składni, jak i do podstawowej semantyki.