El término "ortogonalidad" es un término simple para una noción matemática precisa: los términos del lenguaje forman un álgebra inicial (búsquelo en Wikipedia).
Básicamente significa "hay una correspondencia 1-1 entre sintaxis y significado". Esto significa: hay exactamente una forma de expresar las cosas y, si puede poner alguna expresión en un lugar en particular, entonces puede poner cualquier otra expresión allí también.
Otra forma de pensar en "ortogonal" es que la sintaxis obedece al principio de sustitución. Por ejemplo, si tiene una instrucción con un espacio para una expresión, cualquier expresión se puede colocar allí y el resultado sigue siendo un programa sintácticamente válido. Además, si reemplaza
Quiero enfatizar que "significado" no implica un resultado computacional. Claramente, 1 + 2 y 2 + 1 son iguales a 3. Sin embargo, los términos son distintos e implican un cálculo diferente incluso si tiene el mismo resultado. El significado es diferente, así como dos algoritmos de clasificación son diferentes.
Es posible que haya oído hablar del "árbol de sintaxis abstracta" (AST). La palabra "abstracto" aquí significa precisamente "ortogonal". ¡Técnicamente, la mayoría de los AST no son de hecho abstractos!
¿Quizás has oído hablar del lenguaje de programación "C"? La notación de tipo C no es abstracta. Considerar:
int f(int);
Así que aquí hay una declaración de función que devuelve el tipo int
. El tipo de puntero a esta función viene dado por:
int (*)(int)
Tenga en cuenta que no puede escribir el tipo de la función. ¡La notación de tipo C es una mierda! No es abstracto No es ortogonal. Ahora, supongamos que queremos hacer una función que acepte el tipo anterior en lugar de int:
int (*) ( int (*)(int) )
Todo bien ... pero ... ¿y si queremos devolverlo?
int (*)(int) (*) (int)
Woops! Inválido. Vamos a agregar parens:
(int (*)(int)) (*) (int)
Woops! Eso tampoco funciona. Tenemos que hacer esto (¡es la única forma!):
typedef int (intintfunc*) (int);
intintfunc (*)(int)
Ahora está bien, pero tener que usar un typedef aquí es malo. C apesta. No es abstracto No es ortogonal. Así es como se hace esto en ML, que es:
int -> (int -> int)
Condenamos C en el nivel de sintaxis.
Ok, ahora vamos a flog C ++. Podemos arreglar la estupidez anterior con plantillas y obtener una notación similar a ML (más o menos):
fun<int, int>
fun< fun<int,int>, int>
pero el sistema de tipos real está fundamentalmente viciado por las referencias: si T
es un tipo, ¿entonces es T&
un tipo? La respuesta es imprecisa: en el nivel de sintaxis, si tiene un tipo U = T &, entonces U & está permitido pero solo significa T &: una referencia a una referencia es la referencia original. Esto apesta! Rompe el requisito de unicidad semánticamente. Peor: T & & no está permitido sintácticamente: esto rompe el principio de sustitución. Por lo tanto, las referencias de C ++ rompen la ortogonalidad de dos maneras diferentes, dependiendo del tiempo de unión (análisis o análisis de tipo). Si quieres entender cómo hacer esto bien ... ¡no hay problema con los punteros!
Casi ningún idioma real es ortogonal. Incluso Scheme, que pretende una gran claridad de expresión, no lo es. Sin embargo, se puede considerar que muchos buenos idiomas tienen una "base de características razonablemente similar a la ortogonal" y esa es una buena recomendación para un lenguaje, aplicado tanto a la sintaxis como a la semántica subyacente.