Ventajas de una sintaxis de lenguaje de izquierda a derecha

18

He estado viendo una entrevista con Herb Sutter en Channel9 y mencionó al final del video que la sintaxis del lenguaje de izquierda a derecha estaría en la parte superior de su lista de deseos para un futuro estándar de C ++ (aunque reconoce que modificar C ++ de esa manera prácticamente sería una bestia completamente diferente).

Aparte de:

  • más comprensible para los humanos, más claro a simple vista; por ejemplo

    //C syntax
    
    /*pointer to function taking a pointer to function(which takes 2 integers as 
    
    arguments and returns an int), and an int as arguments and returning an int*/
    
    int (*fp)(int (*ff)(int x, int y), int b)
    
    //Go analogous syntax which is left to write
    
    f func(func(int,int) int, int) int
  • más fácil de analizar (conduce a un mejor soporte de herramientas como se menciona en el video, por ejemplo, refactorización de código)

¿Qué otras ventajas hay con una sintaxis de "izquierda a derecha" en un lenguaje de programación? Solo sé que Pascal and Go emplea ese tipo de sintaxis (y Go ni siquiera hace todo lo que entiendo de esta publicación de blog de la que tomé también los ejemplos) ¿Sería factible tener un lenguaje de programación de sistemas con ese tipo? de sintaxis?

celavek
fuente
1
haskell usa de izquierda a derecha:f :: (Int -> Int -> Int) -> Int -> Int
Karoly Horvath
1
ActionScript, así: function strlen(s:String):int {...}. Además, tipeó cálculo lambda (por lo tanto, Haskell).
outis
2
¿Alguien podría explicar por favor los votos finales :)? No puedo ver ninguna razón para cerrarlo, pero tal vez estoy haciendo una pregunta "incorrecta".
1
No voté para cerrar, pero, el comentario de @Devjosh es pertinente, es más apropiado para los Programadores, espero que alguien lo migre ...
Nim
3
@Frank: y no olvide, en el caso de los punteros de funciones, que la sintaxis es incómoda porque el tipo real está dividido . Eso es un golpe bajo ...
Matthieu M.

Respuestas:

12

La ventaja básica es que el análisis es más simple y único. Tenga en cuenta que después de analizar la línea, el compilador sabrá cuál es el tipo exacto, por lo tanto, a partir de ahí, la forma en que se definió el tipo es irrelevante.

Actualmente, cualquier función que devuelva un argumento de tipo de matriz o tipo de puntero de función es difícil de leer:

// common way of obtaining the static size in elements of an array:
template <typename T, int N>
char (&array_size_impl( T (&)[N] ))[N];
// alternative parser:
template <typename T, int N>               // this would probably be changed also
array_simple_impl function( T [N] & ) char [N] &;

Y habría menos posibilidades de malentendidos (como el análisis más irritante ):

// Current C++
type x( another_type() );      // create an instance x of type passing a
                               // default constructed another_type temporary?
                               // or declare a function x that returns type and takes as argument
                               // a function that has no arguments and returns another_type
// How the compiler reads it:
x function( function() another_type ) type;

// What many people mean:
x type { another_type{} };

Usar un enfoque similar para la inicialización uniforme en C ++ 0x (es decir, {}para identificar la inicialización). Tenga en cuenta que con un enfoque de izquierda a derecha es mucho más claro lo que estamos definiendo. Muchas personas (estoy seguro) han sido mordidas en algún momento por este error de análisis en el pasado (más de una vez), y ese no sería el caso con una sintaxis de izquierda a derecha.

David Rodríguez - dribeas
fuente
¿Qué hay de las expresiones? ¿Cómo afectaría esta "sintaxis de izquierda a derecha" la precedencia del operador y el orden de evaluación?
1
@celavek: Si vuelve a la entrevista, notará que no quiere cambiar la sintaxis completa del idioma, solo las declaraciones y definiciones s. Por supuesto, eso podría permear en otras expresiones, no estoy muy seguro de que la última línea en los ejemplos anteriores sea correcta de izquierda a derecha (piense en cómo se crea el temporal, es posible que deba cambiarse ... en C # eso se resuelve dando dos semánticas al newoperador para structo class, que no es aplicable a C ++, ya que en C ++ no hay distinciones en los tipos de valor / referencia.
David Rodríguez - dribeas
5

Cómo llegamos aquí

La sintaxis de C para declarar puntos de función tenía la intención de reflejar el uso. Considere una declaración de función regular como esta de <math.h>:

double round(double number);

Para tener una variable de punto, puede asignarla con seguridad de tipo usando

fp = round;

deberías haber declarado esa fpvariable de punto de esta manera:

double (*fp)(double number);

Entonces, todo lo que tiene que hacer es ver cómo usaría la función y reemplazar el nombre de esa función con una referencia de puntero, convirtiéndola rounden *fp. Sin embargo, necesita un conjunto adicional de parens, lo que algunos dirían que lo hace un poco más desordenado.

Podría decirse que esto solía ser más fácil en el C original, que ni siquiera tenía la firma de la función, pero no volvamos allí, ¿de acuerdo?

El lugar en el que se vuelve especialmente desagradable es descubrir cómo declarar una función que toma como argumento o devuelve un puntero a una función, o ambas cosas.

Si tuvieras una función:

void myhandler(int signo);

podría pasarlo a la función de señal (3) de esta manera:

signal(SIGHUP, myhandler);

o si quieres conservar el antiguo controlador, entonces

old_handler = signal(SIGHUP, new_handler);

Lo cual es bastante fácil. Lo que es bastante fácil, ni bonito ni fácil, es hacer las declaraciones correctas.

signal(int signo, ???)

Bueno, simplemente regrese a su declaración de función e intercambie el nombre por una referencia de punto:

signal(int sendsig, void (*hisfunc)(int gotsig));

Debido a que no está declarando gotsig, puede que le resulte más fácil de leer si omite:

signal(int sendsig, void (*hisfunc)(int));

O tal vez no. :(

Excepto que eso no es lo suficientemente bueno, porque la señal (3) también devuelve el controlador antiguo, como en:

old_handler = signal(SIGHUP, new_handler);

Así que ahora tienes que descubrir cómo declarar todo eso.

void (*old_handler)(int gotsig);

es suficiente para la variable a la que va a asignar. Tenga en cuenta que no está declarando realmente gotsigaquí, solo old_handler. Entonces esto es realmente suficiente:

void (*old_handler)(int);

Eso nos lleva a una definición correcta de señal (3):

void (*signal(int signo, void (*handler)(int)))(int);

Typedefs al rescate

En este momento, creo que todos estarán de acuerdo en que eso es un desastre. A veces es mejor nombrar tus abstracciones; a menudo, de verdad. Con la derecha typedef, esto se vuelve mucho más fácil de entender:

typedef void (*sig_t) (int);

Ahora su propia variable de controlador se convierte en

sig_t old_handler, new_handler;

y su declaración para la señal (3) se vuelve justa

sig_t signal(int signo, sig_t handler);

que de repente es comprensible Deshacerse de los * también elimina algunos de los paréntesis confusos (y dicen que los padres siempre hacen las cosas más fáciles de entender, ¡ja!). Su uso sigue siendo el mismo:

old_handler = signal(SIGHUP, new_handler);

pero ahora tiene la oportunidad de comprender las declaraciones de old_handler, new_handlere incluso signalcuando las encuentra por primera vez o necesita escribirlas.

Conclusión

Resulta que muy pocos programadores de C son capaces de idear las declaraciones correctas para estas cosas por sí mismos sin consultar los materiales de referencia.

Lo sé, porque una vez tuvimos esta misma pregunta en nuestras preguntas de entrevista para personas que trabajan en el núcleo y el controlador del dispositivo. :) Claro, perdimos muchos candidatos de esa manera cuando se estrellaron y quemaron en la pizarra. Pero también evitamos contratar personas que afirmaran que tenían experiencia previa en esta área pero que en realidad no podían hacer el trabajo.

Sin embargo, debido a esta dificultad generalizada, probablemente no solo sea sensato, sino que sea razonable tener una forma de hacer todas esas declaraciones que ya no requieren que seas un programador geek de triple alfa sentado tres sigmas por encima de la media solo para usar esto tipo de cosas cómodamente.

tchrist
fuente
1
Contribuido, difícil de seguir ... +1 por el esfuerzo, ya que ilustra el punto de que a veces es difícil acertar en C.
celavek
4

Creo que de alguna manera perdiste el punto cuando te enfocaste en el bit de izquierda a derecha.

El problema de C y C ++ es la gramática horrenda que tienen, que es difícil de leer (humanos) y analizar (herramientas).

Teniendo un más gramática consistente (o regular ) hace que sea más fácil para ambos. Y el análisis más fácil significa herramientas más sencillas: la mayoría de las herramientas actuales no funcionan correctamente con C ++, ni siquiera el último complemento de Eclipse, ya que intentaron reinventar la rueda ... y fallaron, y probablemente tengan más personas que el proyecto de sistema operativo promedio.

Así que probablemente lo has clavado cuando te enfocas en leer y analizar ... y eso es un gran problema :)

Matthieu M.
fuente
Es una gran sorpresa que Eclipse aún no pueda analizar cosas como las declaraciones anteriores. ¿Por qué no usan un analizador C real gcc?
tchrist
@tchrist: He detectado dos problemas con Eclipse, y ambos parecen vinculados a macros. Quizás sea más un problema de preprocesador que la generación AST.
Matthieu M.