Термин «ортогональность» - это термин непрофессионала для точного математического понятия: языковые термины образуют начальную алгебру (см. Ее в Википедии).
Это в основном означает «есть соответствие 1-1 между синтаксисом и значением». Это означает: есть только один способ выразить вещи, и, если вы можете поместить какое-то выражение в определенное место, то вы можете поместить и любое другое выражение там.
Другой способ думать об «ортогональности» состоит в том, что синтаксис подчиняется принципу подстановки. Например, если у вас есть оператор со слотом для выражения, тогда любое выражение может быть помещено туда, и результат все еще остается синтаксически допустимой программой. Кроме того, если вы замените
Я хочу подчеркнуть, что «значение» не подразумевает вычислительный результат. Ясно, что 1 + 2 и 2 + 1 оба равны 3. Однако термины различны и подразумевают другое вычисление, даже если оно имеет одинаковый результат. Значение отличается, так же как два алгоритма сортировки различны.
Возможно, вы слышали об «абстрактном синтаксическом дереве» (AST). Слово «абстрактный» здесь означает именно «ортогональный». Технически большинство АСТ на самом деле не абстрактны!
Возможно, вы слышали о языке программирования "C"? Обозначение типа C не является абстрактным. Рассматривать:
int f(int);
Итак, вот объявление функции, возвращающее тип int
. Тип указателя на эту функцию определяется как:
int (*)(int)
Обратите внимание, вы не можете написать тип функции! Обозначение типа C - отстой! Это не абстрактно. Это не ортогонально. Теперь предположим, что мы хотим создать функцию, которая принимает вышеуказанный тип вместо int:
int (*) ( int (*)(int) )
Все хорошо .. но .. что если мы хотим вернуть это вместо:
int (*)(int) (*) (int)
Woops! Недействительным. Давайте добавим парены:
(int (*)(int)) (*) (int)
Woops! Это тоже не работает. Мы должны сделать это (это единственный способ!):
typedef int (intintfunc*) (int);
intintfunc (*)(int)
Теперь все в порядке, но использование typedef здесь плохо. С отстой. Это не абстрактно. Это не ортогонально. Вот как вы делаете это в ML, а именно:
int -> (int -> int)
Мы осуждаем C на уровне синтаксиса.
Хорошо, теперь давайте выпустить C ++. Мы можем исправить вышеуказанную глупость с помощью шаблонов и получить обозначение, подобное ML (более или менее):
fun<int, int>
fun< fun<int,int>, int>
но действительная система типов в корне неверна ссылками: если T
это тип, то есть T&
ли тип? Ответ неопределенный: на уровне синтаксиса, если у вас есть тип U = T &, тогда U & разрешен, но это просто означает T &: ссылка на ссылку является исходной ссылкой. Это отстой! Это семантически нарушает требование уникальности. Хуже того, T & & не допускается синтаксически: это нарушает принцип замещения. Таким образом, ссылки на C ++ нарушают ортогональность двумя различными способами, в зависимости от времени привязки (анализ или анализ типов). Если вы хотите понять, как это сделать правильно ... с указателями проблем нет!
Практически нет реальных языков ортогональных. Даже Схема, которая претендует на большую ясность выражения, - нет. Однако многие хорошие языки могут быть оценены как имеющие «достаточно близкие к ортогональным признакам свойства», и это хорошая рекомендация для языка, применяемого как к синтаксису, так и к базовой семантике.