¿Qué hacen los diseñadores de idiomas para decidir o probar que una característica particular funciona correctamente?

11

Estoy interesado en el diseño del lenguaje y, en general, puedo razonar fácilmente sobre características ampliamente conocidas (por ejemplo, herencia, polimorfismo, delegados, lambdas, capturas, recolección de basura, excepciones, genéricos, variación, reflexión, etc.), sus interacciones en un lenguaje particular, las formas en que pueden implementarse, sus limitaciones, etc.

En los últimos meses, comencé a leer sobre Rust, que tiene un sistema de propiedad que garantiza la seguridad de la memoria y la gestión determinista de los recursos al obligar a que la vida útil de los objetos sea estáticamente verificable. Desde la perspectiva de un simple usuario del idioma, podría recoger el sistema casi de inmediato.

Sin embargo, desde la perspectiva de un diseñador de idiomas, me llevó un tiempo darme cuenta de por qué las cosas en Rust son exactamente como son. No pude entender de inmediato el razonamiento detrás de algunas restricciones del sistema de propiedad, hasta que me obligué a presentar casos que violarían la integridad de un sistema si no tuviera esos aspectos.

Mi pregunta principal no tiene nada que ver con Rust y su propiedad específicamente, pero siéntase libre de usarlo como ejemplo en sus comentarios / respuestas.

Cuando los diseñadores de idiomas diseñan una nueva característica, ¿qué metodología o proceso utilizan para decidir que la característica funciona correctamente?

Por "nuevo" quiero decir que no es algo que ya se haya probado en los idiomas existentes (y, por lo tanto, la mayor parte del trabajo ha sido realizado por otros diseñadores). Por "funciona correctamente" me refiero a que la función resuelve el problema deseado correctamente, y es razonablemente a prueba de balas. Por "razonablemente a prueba de balas" quiero decir que no se puede escribir ningún código en el idioma o en un subconjunto particular del idioma (por ejemplo, un subconjunto sin código "inseguro") que viole la integridad de la función.

  • ¿Es un proceso de prueba y error, en el sentido de que se te ocurre una forma simple de la función, luego intentas encontrar formas de violarla, luego parchearla si la violas con éxito y luego repetir? Y luego, cuando no puede pensar en ninguna otra violación posible, ¿espera que no quede nada y lo llama un día?

  • ¿O hay una forma formal de demostrar realmente (en el sentido matemático de la palabra) que su característica funciona y luego usar esa prueba para obtener la característica correcta (o en su mayoría correcta) desde el principio?

(Debo mencionar que tengo experiencia en ingeniería, no ciencias de la computación. Entonces, si me estoy perdiendo algo que sería obvio para la gente de CS, no dude en señalarlo).

Theodoros Chatzigiannakis
fuente
Cuando dice "diseñador de idiomas", ¿se refiere a una persona que crea el compilador, o simplemente la sintaxis, o ambas?
Snoop
66
@StevieV: el diseño del lenguaje es diferente e independiente de la implementación. Por ejemplo, Lisp fue diseñado por John McCarthy como una alternativa más fácil de entender al cálculo λ. Sin embargo, no lo implementó. De hecho, cuando su estudiante Steve Russell quería implementar Lisp, McCarthy le dijo que creía que era imposible implementar Lisp. APL fue diseñado como un lenguaje para la enseñanza de las matemáticas. Más tarde, IBM lo usó para especificar formalmente el comportamiento del Sistema / 360, para el cual el lenguaje obtuvo varias extensiones. En este momento, todavía no se implementó. Plankalkül fue diseñado por Konrad
Jörg W Mittag el
44
Zuse 1942-1946, pero solo se implementó en 1975. Niklaus Wirth primero diseñó completamente sus idiomas, y solo los implementó después de que terminó con el diseño (y escribió el primer compilador en el idioma en sí para tener una idea de qué tan bien era el idioma diseñado, luego hizo que sus alumnos tradujeran manualmente el compilador a otro idioma para el arranque). Nunca se implementan muchos más idiomas académicos, solo están diseñados para probar un punto o experimentar con alguna característica del lenguaje de manera abstracta. Smalltalk fue creado como resultado de una apuesta mutua: Alan Kay apostó a que podía
Jörg W Mittag el
3
Al diseñar un lenguaje orientado a objetos en una sola página de papel, Dan Ingalls apostó a que podría implementar ese lenguaje en un par de días. (¡Y lo hizo en BASIC, de todos los idiomas!) Los idiomas son objetos matemáticos que existen independientemente de sus compiladores / intérpretes. Y pueden ser diseñados, estudiados y discutidos independientemente de cualquier implementación física.
Jörg W Mittag
3
Debe leer: Godel, Escher, Bach . A veces es un poco extraño, pero hacia el final entra en gran parte del trabajo de Turing y Godel que afecta en gran medida la formalidad del diseño del lenguaje.
RubberDuck

Respuestas:

6

Tengo problemas para encontrar la referencia exacta en este momento, pero hace un tiempo vi varios videos de Simon Peyton Jones , quien fue uno de los principales contribuyentes al diseño de Haskell. Por cierto, es un excelente orador en teoría de tipos, diseño de lenguaje y similares, y tiene muchos videos disponibles de forma gratuita en youtube.

Haskell tiene una representación intermedia que es esencialmente cálculo lambda aumentado con algunas cosas simples para que sea más fácil trabajar con él. El cálculo Lambda se ha utilizado y probado desde que una computadora era solo una persona que calculaba cosas. Un punto interesante que Simon Peyton Jones hace a menudo es que cada vez que hacen algo salvaje y loco con el idioma, él sabe que es fundamentalmente sólido cuando finalmente se reduce a ese idioma intermedio.

Otros lenguajes no son tan rigurosos, sino que favorecen la facilidad de uso o implementación. Hacen lo mismo que otros programadores para obtener un código de alta calidad: seguir buenas prácticas de codificación y probarlo hasta la muerte. Estoy seguro de que una característica como la semántica de propiedad de Rust obtiene tanto análisis formales como pruebas para encontrar casos de esquina olvidados. A menudo, características como esa comienzan como la tesis de posgrado de alguien.

Karl Bielefeldt
fuente
2
Creo que la referencia que está buscando está en una de las series "Aventuras con tipos en Haskell", probablemente esta dada el contenido del tablero en la imagen en miniatura ...
Jules
8

Entonces, para el diseño del lenguaje , hay pruebas (o errores). Por ejemplo, sistemas de tipo. Tipos y lenguajes de programación es el libro canónico que describe los sistemas de tipos, y se enfoca en probar la corrección e integridad del sistema de tipos. Las gramáticas tienen un análisis similar y los algoritmos (como el sistema de propiedad que usted describe) tienen el suyo.

Para la implementación del lenguaje , es un código como cualquier otro. Escribes pruebas unitarias. Escribes pruebas de integración. Haces revisiones de código.

Lo único que hace que los idiomas sean especiales es que son (casi siempre) infinitos. Literalmente no puede probar todas las entradas. Y (idealmente) son utilizados por toneladas de personas, haciendo cosas raras e interesantes, por lo que eventualmente se encontrará cualquier error en el lenguaje .

Prácticamente , relativamente pocos idiomas usan pruebas para verificar su funcionalidad, y terminan con una mezcla de las opciones que mencionas.

Telastyn
fuente
44
The only thing that makes languages special is that they are (almost always) infinite. You literally cannot test all inputs.¿Es eso realmente tan especial? Ese parece ser el caso común para mí. Por ejemplo, una función que toma una lista como argumento también tiene un número infinito de entradas. Para cualquier tamaño n que elija, hay una lista de tamaños n + 1.
Doval
@doval - y cadenas también, supongo. Un buen punto
Telastyn
4

Lo primero y más difícil que un diseñador de idiomas debe tener en cuenta al introducir nuevas funciones es mantener su lenguaje coherente:

  • ¿Cómo se puede integrar en la gramática del lenguaje sin romper el código existente (esto se puede probar matemáticamente)
  • cómo se relaciona con las características existentes (por ejemplo, si tiene matrices fijas indexadas 0..n-1, no introducirá una nueva característica de matriz variable indexada 1..n) (esa es la parte artística del diseño)
  • cómo se puede implementar la característica en toda la cadena de herramientas para que el ecosistema, los fabricantes de herramientas y los programadores puedan absorber la nueva característica (la viabilidad se puede demostrar con una prueba de concepto, pero la implementación completa es un enfoque similar a la programación)

Para guiarse en este asunto, un diseñador se basa en un conjunto de reglas y principios de diseño. Este enfoque está muy bien descrito en " El diseño y la evolución de C ++ " de Bjarne Stroustrup , uno de los pocos libros dedicados al diseño del lenguaje. Lo que es muy interesante es ver que los idiomas rara vez se diseñan en el vacío, y el diseñador también observa cómo sus idiomas han implementado características similares. Otra fuente (en línea y gratuita) son los principios de diseño para el lenguaje java .

Si observa los procedimientos públicos de los comités de normalización, verá que es más un proceso de error de prueba. Aquí un ejemplo en el módulo C ++, un concepto completamente nuevo que se introducirá en la próxima versión del lenguaje. Y aquí un análisis redactado después de algunos cambios de idioma , para evaluar su éxito. Y aquí el Proceso de la Comunidad Java para definir nuevas especificaciones Java, como una nueva API . Verá que este trabajo es realizado por varios expertos que redactan creativamente un documento conceptual y una primera propuesta. Luego, estas propuestas son revisadas por una comunidad / comité más grande que puede enmendar la propuesta para asegurar un mayor grado de consistencia.

Christophe
fuente
4

¿Cómo probar las características del lenguaje de programación? Es una muy buena pregunta, y no estoy seguro de que el estado del arte esté a la altura.

Cada nueva característica puede interactuar con todas las demás características. (Esto afecta el idioma, los documentos, los compiladores, los mensajes de error, los IDE, las bibliotecas, etc.) ¿Las características se combinan para abrir un vacío? Para crear casos extremos desagradables?

Incluso los diseñadores de idiomas muy inteligentes que trabajan duro para mantener la solidez del tipo descubren violaciones como este error de óxido . El sistema de tipos de Rust no es tan obvio para mí, pero creo que en este caso tener el tiempo de vida del valor de la pista del sistema de tipos significa un "subtipo" (subranges) de por vida que choca con las expectativas de subtipos, coerciones, referencias y mutabilidad ordinarias, creando un vacío donde statictoda la vida ref puede apuntar a un valor asignado por pila y luego convertirse en una referencia colgante.

Por "funciona correctamente" me refiero a que la función resuelve el problema deseado correctamente, y es razonablemente a prueba de balas.

Para los lenguajes destinados a ser lenguajes de producción , es decir, utilizados por muchos programadores para crear software de producción confiable, "funciona correctamente" debe significar además resolver el problema previsto correctamente para la audiencia destinataria.

En otras palabras, la usabilidad es tan importante para el diseño del lenguaje como lo es para otros tipos de diseño. Esto implica (1) diseño de usabilidad (por ejemplo, conocer a su audiencia) y (2) pruebas de usabilidad.

Un artículo de ejemplo sobre este tema es "Los programadores son personas, también , los diseñadores de lenguaje de programación y API pueden aprender mucho del campo del diseño de factores humanos".

Un ejemplo de pregunta SE sobre este tema es ¿ Se ha probado la usabilidad de algún lenguaje de programación?

Un ejemplo de prueba de usabilidad consideró extender una función de iteración de lista (no recuerdo en qué idioma) tomar múltiples listas. ¿La gente esperaba que recorriera las listas en paralelo o a través del producto cruzado? Los diseñadores del lenguaje quedaron sorprendidos por los resultados de la prueba de usabilidad.

Lenguajes como Smalltalk, Python y Dart fueron diseñados con énfasis en la usabilidad. Claramente Haskell no estaba.

Jerry101
fuente
Haskell es realmente bastante utilizable. Solo es difícil de aprender porque es un paradigma totalmente diferente a Python / C / Java, etc. Pero como lenguaje es bastante fácil de usar.
punto