¿Qué debería estar demostrando una prueba de corrección para un typechecker?


11

He estado programando durante varios años, pero no estoy muy familiarizado con el CS teórico. Recientemente he estado tratando de estudiar lenguajes de programación y, como parte de eso, la verificación de tipos y la inferencia.

Mi pregunta es, si trato de escribir una inferencia de tipos y un programa de verificación para un lenguaje de programación, y deseo probar que mi typechecker funciona, ¿cuál es exactamente la prueba que estoy buscando?

En lenguaje sencillo, quiero que mi verificador de tipos pueda identificar cualquier error en un fragmento de código que pueda ocurrir en tiempo de ejecución. Si tuviera que usar algo como Coq para probar que mi implementación es correcta, ¿qué intentará mostrar exactamente esta "prueba de corrección"?


Tal vez pueda aclarar si desea saber (1) si su implementación implementa un sistema de escritura , o (2) si su sistema de escritura T evita los errores que cree que debería. Son preguntas diferentes. TT
Martin Berger

1
@ MartinBerger: Ah, parece que me he saltado esa diferencia. Mi pregunta real probablemente significaba hacer ambas. El contexto es que estoy tratando de construir un lenguaje, y para eso estaba escribiendo un typechecker. Y la gente me pidió que usara un algoritmo probado. Estaba interesado en ver lo difícil que sería "probar" que el algoritmo y el typechecker que estaba usando eran "correctos". De ahí la ambigüedad en mi pregunta.
Vivek Ghaisas

2
(1) es realmente una pregunta en la verificación del programa y tiene poco que ver con la escritura. Solo necesita demostrar que su implementación cumple con sus especificaciones. En cuanto a (2), primero defina lo que significa ser un error de tipo inmediato (por ejemplo, términos como 2 + "hello"ese están 'atascados'). Una vez formalizado esto, puede probar el teorema de solidez de tipo. Eso significa que ningún programa tipificable puede evolucionar a un error de tipo inmediato. Formalmente, demuestra que si un programa es tipificable y para cualquier n : si M ejecuta n pasos para convertirse en N , entonces N no tiene un error de tipo inmediato. (1/2)MnMnNN
Martin Berger

1
Esto se prueba típicamente por inducción en y en la derivación de la tipificación judegement. (2/2)n
Martin Berger

¡Gracias! Según su explicación, parece que (2) es de hecho lo que estaba buscando. ¿Podría por favor hacer eso una respuesta? (Y tal vez agregue cualquier detalle que considere útil). ¡Aceptaría eso como respuesta! :)
Vivek Ghaisas

Respuestas:


10

La pregunta se puede interpretar de dos maneras:

  • ¿La implementación implementa un determinado sistema de escritura ?T
  • ¿Si el sistema de escritura previene los errores que crees que debería?T

La primera es realmente una pregunta en la verificación del programa y tiene poco que ver con la escritura. Solo necesita mostrar que su implementación cumple con sus especificaciones, vea la respuesta de Andrej.

Déjame hablar sobre la pregunta posterior. Como dijo Andrej, desde un punto de vista abstracto, un sistema de mecanografía parece imponer propiedades en los programas. En la práctica, su sistema de escritura busca evitar que ocurran errores, lo que significa que los programas que se pueden escribir no deben exhibir la clase de errores de interés. Para demostrar que T hace lo que crees que debería hacer, debes hacer dos cosas.TT

  • Primero, usted define formalmente lo que significa que un programa tenga un error de escritura inmediato . Hay muchas formas en que esto se puede definir, depende de usted. Por lo general, queremos evitar programas como 2 + "hello". En otras palabras, debe definir un subconjunto de programas, llámelos Malo , que contiene exactamente los programas con un error de escritura inmediato.

  • ΓM:α.MαΓ

    ΓM:αMNN

    Cómo probar este teorema depende de los detalles del idioma, el sistema de escritura y la elección de Bad .

MMNM

  • ΓM:αMNΓN:α

  • ΓM:αM

Tenga en cuenta que no todos los sistemas de mecanografía tienen "reducción de sujeto", por ejemplo, tipos de sesión. En este caso, se requieren técnicas de prueba más sofisticadas.


20

¡Buena pregunta! Pregunta qué esperamos de los tipos en un idioma escrito.

Primero tenga en cuenta que podemos escribir cualquier lenguaje de programación con la unidad : simplemente elija una letra, diga Uy diga que cada programa tiene tipo U. Esto no es terriblemente útil, pero tiene sentido.

eAeAAint

No hay fin a lo expresivos que pueden ser sus tipos. En principio, podrían ser cualquier tipo de enunciados lógicos, podrían usar la teoría de categorías y otras cosas, etc. Por ejemplo, los tipos dependientes le permitirán expresar cosas como "esta función asigna listas a listas para que la salida sea una entrada ordenada". Puede ir más allá, en este momento estoy escuchando una charla sobre "lógicas de separación concurrente" que le permite hablar sobre cómo funcionan los programas concurrentes con estado compartido. Cosas lujosas.

El arte de los tipos en el diseño del lenguaje de programación es uno de equilibrar la expresividad y la simplicidad :

  • tipos más expresivos nos permiten explicar con más detalle (a nosotros mismos y al compilador) lo que se supone que está sucediendo
  • los tipos más simples son más fáciles de entender y pueden automatizarse más fácilmente en el compilador. (A las personas se les ocurren tipos que esencialmente requieren un asistente de prueba y la entrada del usuario para hacer la verificación de tipos).

La simplicidad no debe subestimarse, ya que no todos los programadores tienen un doctorado en teoría de lenguajes de programación.

Así que volvamos a su pregunta: ¿cómo sabe que su sistema de tipos es bueno ? Bueno, pruebe los teoremas que muestran que sus tipos están equilibrados. Habrá dos tipos de teoremas:

  1. Teoremas que dicen que tus tipos son útiles . Saber que un programa tiene un tipo debería implicar algunas garantías, por ejemplo, que el programa no se atascará (eso sería un teorema de seguridad ). Otra familia de teoremas conectaría los tipos con los modelos semánticos para que podamos comenzar a usar matemáticas reales para probar cosas sobre nuestros programas (esos serían los teoremas de adecuación y muchos otros). La unidad anterior es mala porque no tiene teoremas tan útiles.

  2. Teoremas que dicen que tus tipos son simples . Uno básico sería que es decidible si una expresión dada tiene un tipo dado. Otra característica de simplicidad es dar un algoritmo para inferir un tipo. Otros teoremas sobre la simplicidad serían: que una expresión tiene como máximo un tipo, o que una expresión tiene un tipo principal (es decir, el "mejor" entre todos los tipos que tiene).

Es difícil ser más específico porque los tipos son un mecanismo muy general. Pero espero que veas a qué debes disparar. Como la mayoría de los aspectos del diseño del lenguaje de programación, no existe una medida absoluta de éxito. En cambio, hay un espacio de posibilidades de diseño, y lo importante es comprender en qué parte del espacio se encuentra o quiere estar.


Gracias por esa respuesta detallada! Sin embargo, todavía no estoy seguro de la respuesta a mi pregunta. Como ejemplo concreto, tomemos C: un lenguaje de tipo estático con un sistema de tipos lo suficientemente simple. Si escribiera un typechecker para C, ¿cómo probaría que mi typechecker es "correcto"? ¿Cómo cambia esta respuesta si, en cambio, escribí un corrector de tipo para Haskell, digamos HM? ¿Cómo probaría ahora la "corrección"?
Vivek Ghaisas

1
TeATeA

Recomendaría hacer 2. y 3. como una combinación. Además, eche un vistazo a CompCert .
Andrej Bauer

1
TeAeAe

AAe

5

Hay algunas cosas diferentes a las que podría referirse con "demostrar que mi typechecker funciona". Lo cual, supongo, es parte de lo que hace tu pregunta;)

La mitad de esta pregunta está demostrando que su teoría de tipos es lo suficientemente buena como para probar las propiedades del lenguaje. La respuesta de Andrej aborda esta área muy bien. La otra mitad de la pregunta es, suponiendo que el lenguaje y su sistema de tipos ya estén fijos, ¿cómo puede probar que su verificador de tipos en particular implementa el sistema de tipos correctamente? Hay dos perspectivas principales que puedo ver aquí.

Una es: ¿cómo podemos confiar en que alguna implementación en particular coincida con sus especificaciones? Dependiendo del grado de garantías que desee, es posible que esté satisfecho con un gran conjunto de pruebas, o puede que desee algún tipo de verificación formal, o más probablemente una combinación de ambos . La ventaja de esta perspectiva es que realmente resalta la importancia de establecer límites en las afirmaciones que está haciendo: ¿qué significa exactamente "correcto"? ¿Qué parte del código se verifica, frente a qué parte es la TCB supuestamente correcta? La desventaja es que pensar demasiado en esto lleva a uno a los agujeros de conejo filosóficos , bueno, "inconveniente" si no disfrutas de esos agujeros de conejo.

La segunda perspectiva es una versión más matemática de la corrección. Cuando se trata de idiomas en matemáticas, a menudo configuramos "modelos" para nuestras "teorías" (o viceversa) y luego intentamos demostrar: (a) todo lo que podemos hacer en la teoría que podemos hacer en el modelo, y (b) todo lo que podemos hacer en el modelo podemos hacerlo en la teoría. (Estos son Solidez y Completitudteoremas Cuál depende de si usted "comenzó" a partir de la teoría sintáctica o del modelo semántico.) Con esta mentalidad podemos pensar en su implementación de verificación de tipo como un modelo particular para la teoría de tipo en cuestión. Por lo tanto, querrá probar esta correspondencia bidireccional entre lo que puede hacer su implementación y lo que la teoría dice que debería poder hacer. La ventaja de esta perspectiva es que realmente se centra en si ha cubierto todos los casos de esquina, si su implementación está completa en el sentido de no omitir ningún programa que debería aceptar como tipo seguro, y si su implementación es sólida la sensación de no dejar entrar ningún programa que debería rechazar como mal escrito. La desventaja es que es probable que su prueba de correspondencia esté bastante separada de la implementación misma,


No estoy seguro de poder estar de acuerdo con "lo bueno de esta perspectiva es que realmente se centra en si ha cubierto todos los casos de esquina", especialmente si el modelo es solo sólido, pero no completo. Propondría una perspectiva diferente: pasar por un modelo es una técnica de prueba contingente que utiliza por varias razones, por ejemplo, porque el modelo es más simple. No hay nada filosóficamente más digno sobre pasar por un modelo; en última instancia, desea saber sobre el ejecutable real y su comportamiento.
Martin Berger

Pensé que "modelo" y "teoría" tenían un sentido amplio, y Wren solo enfatizaba la importancia de tratar de establecer una correspondencia bidireccional a través de un "teorema de solidez + integridad". (También creo que esto es importante, e hice un comentario en la publicación de Andrej). Es cierto que en algunas situaciones solo podremos probar un teorema de solidez (o un teorema de integridad, dependiendo de su perspectiva), pero teniendo ambas direcciones en mente es una restricción metodológica útil.
Noam Zeilberger

1
@NoamZeilberger "La pregunta es", dijo Martin, "si puedes hacer que las palabras signifiquen tantas cosas diferentes".
Martin Berger

Cuando aprendí acerca de los sistemas de mecanografía y la semántica del lenguaje de programación, me di cuenta de que los modelos son meramente técnicas de prueba sobre la semántica operativa, en lugar de terminar en sí mismos, sublimemente liberadores.
Martin Berger

1
Relacionar diferentes modelos a través de la solidez y la integridad es una metodología científica importante para la transferencia de conocimiento.
Martin Berger
Al usar nuestro sitio, usted reconoce que ha leído y comprende nuestra Política de Cookies y Política de Privacidad.
Licensed under cc by-sa 3.0 with attribution required.