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).
fuente
Respuestas:
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.
fuente
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.
fuente
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.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:
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.
fuente
¿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
static
toda la vida ref puede apuntar a un valor asignado por pila y luego convertirse en una referencia colgante.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.
fuente