O termo "ortogonalidade" é um termo leigo para uma noção matemática precisa: os termos da linguagem formam uma álgebra inicial (consulte na Wikipedia).
Basicamente, significa "existe uma correspondência 1-1 entre sintaxe e significado". Isso significa: existe exatamente uma maneira de expressar as coisas e, se você pode colocar alguma expressão em um determinado local, também pode colocar qualquer outra expressão lá.
Outra maneira de pensar em "ortogonal" é que a sintaxe obedece ao princípio da substituição. Por exemplo, se você tiver uma instrução com um slot para uma expressão, qualquer expressão poderá ser colocada lá e o resultado ainda será um programa sintaticamente válido. Além disso, se você substituir
Quero enfatizar que "significado" não implica resultado computacional. Claramente, 1 + 2 e 2 + 1 são iguais a 3. No entanto, os termos são distintos e implicam um cálculo diferente, mesmo que tenha o mesmo resultado. O significado é diferente, assim como dois algoritmos de classificação são diferentes.
Você pode ter ouvido falar em "árvore de sintaxe abstrata" (AST). A palavra "abstrato" aqui significa precisamente "ortogonal". Tecnicamente, a maioria dos AST não é de fato abstrata!
Talvez você já tenha ouvido falar da linguagem de programação "C"? A notação de tipo C não é abstrata. Considerar:
int f(int);
Então, aqui está uma declaração de função retornando o tipo int
. O tipo de um ponteiro para esta função é dado por:
int (*)(int)
Note que você não pode escrever o tipo da função! A notação do tipo C é péssima! Não é abstrato. Não é ortogonal. Agora, suponha que desejemos criar uma função que aceite o tipo acima, em vez de int:
int (*) ( int (*)(int) )
Tudo bem .. mas .. e se quisermos devolvê-lo:
int (*)(int) (*) (int)
Woops! Inválido. Vamos adicionar parênteses:
(int (*)(int)) (*) (int)
Woops! Isso também não funciona. Temos que fazer isso (é a única maneira!):
typedef int (intintfunc*) (int);
intintfunc (*)(int)
Agora está tudo bem, mas ter que usar um typedef aqui é ruim. C é péssimo. Não é abstrato. Não é ortogonal. Veja como você faz isso no ML, que é:
int -> (int -> int)
Condenamos C no nível da sintaxe.
Ok, agora vamos flogar C ++. Podemos corrigir a estupidez acima com modelos e obter uma notação de tipo ML (mais ou menos):
fun<int, int>
fun< fun<int,int>, int>
mas o sistema de tipos real é fundamentalmente falho por referências: se T
é um tipo, então é T&
um tipo? A resposta é waffly: no nível da sintaxe, se você possui um tipo U = T &, então U & é permitido, mas significa apenas T &: uma referência a uma referência é a referência original. Isso é péssimo! Ele quebra o requisito de exclusividade semanticamente. Pior: T& & não é permitido sintaticamente: isso quebra o princípio da substituição. Portanto, as referências C ++ quebram a ortogonalidade de duas maneiras diferentes, dependendo do tempo de ligação (análise ou análise de tipo). Se você quer entender como fazer isso direito .. não há problema com ponteiros!
Quase nenhuma linguagem real é ortogonal. Mesmo Scheme, que finge grande clareza de expressão, não é. No entanto, muitas linguagens boas podem ser consideradas como tendo "uma base razoavelmente próxima da característica ortogonal" e essa é uma boa recomendação para uma linguagem, aplicada tanto à sintaxe quanto à semântica subjacente.