¿Qué tipo de comprobaciones realiza Raku en tiempo de compilación? ¿Puede eso cambiar en el futuro?

Aug 19 2020

Actualmente (a partir de agosto de 2020) Rakudo no comprueba los valores de retorno de las funciones en tiempo de compilación; es decir, no proporciona garantías estáticas de que las funciones satisfagan sus restricciones de retorno. Concretamente, las dos funciones siguientes se compilan como Raku:

sub get-int(--> Int) { 'bug' }
sub get-int($a --> Int} { when $a == 5 { 'Rare bug' }
   default      { 42 }
}

Tengo dos preguntas relacionadas:

  1. ¿Hay alguna forma de saber qué control de tipos (si lo hay) se lleva a cabo actualmente en tiempo de compilación? (O a través de una lista que alguien ha escrito, en algún lugar de los documentos, o en un lugar central en la fuente de Rakudo) ¿O es más ad hoc que eso?

  2. ¿Es esta falta de tiempo de compilación una decisión de diseño intencional? ¿O agregar más verificación de tipo estática es algo que sería bueno tener algún día, pero que aún no se ha implementado?

(Estoy familiarizado con la gran respuesta de Johnathan a Las penalizaciones de rendimiento para tipos / restricciones en Raku ? , que establece que "Raku exige que las restricciones de tipo escritas en el programa se apliquen en tiempo de ejecución a más tardar ". Esa respuesta describe varias formas de evitar la ejecución - costos de tiempo de las comprobaciones de tipo, pero no describe qué comprobaciones de tipo, si las hay, se realizan en tiempo de compilación (¡lo que sin duda evitaría los costes de ejecución!).

Respuestas

12 JonathanWorthington Aug 19 2020 at 12:04

Actualmente, se realiza muy poca verificación de tipos en tiempo de compilación; lo que ocurre principalmente como efecto secundario del optimizador estático. Las comprobaciones de hoy son en gran parte sobre llamadas a subrutinas, donde:

  • Podemos determinar la aridad de la llamada y saber que el número de argumentos pasados ​​nunca coincidirá
  • Tenemos argumentos literales y podemos ver que nunca coincidirían con la firma.

Este es un remanente de cuando el optimizador estático hizo más trabajo de alineación. En estos días, solo inserta operadores nativos en tiempo de compilación y deja el resto para el optimizador dinámico de la VM, que es mucho más capaz de alinear y también puede desconectar (lo que permite la optimización especulativa, pero también significa que se pueden recuperar los seguimientos de pila originales, mientras que optimizador estático perdió esta información).

Se considera deseable hacer más en tiempo de compilación, sin embargo, hay algunos aspectos prácticos a considerar.

  1. La introducción de comprobaciones adicionales también puede provocar la rotura del código que funcionaba antes. Considere un módulo con una ruta de código que fallaría en una verificación de tiempo de compilación más estricta, pero que se está utilizando en sistemas que nunca se ejecutan en ese caso. Si comenzó a fallar al compilar en versiones más nuevas del compilador, entonces sería imposible implementar ese sistema después de una actualización del compilador. En general, esto significa que las verificaciones realizadas deben cambiar en los cambios de versión de idioma. (Esto aún significa que las personas deben declarar la versión del idioma en la que escriben cuando escriben código, claro).
  2. El hecho de que se realicen más comprobaciones en tiempo de compilación "evitará ciertamente los costes de ejecución" puede ser cierto, pero no es trivial razonar al respecto. Un tiempo de ejecución administrado no puede confiar ciegamente en las promesas hechas en el código de bytes que se le da, ya que eso podría conducir a violaciones de seguridad de la memoria (que conducen a SIGSEGV o peor). Esto es claramente cierto en un lenguaje como Raku, donde la semántica de la verificación de tipos es programable, pero es cierto en la JVM, CLR, etc. Las mayores ganancias relacionadas con los tipos en Raku provienen del uso de tipos nativos, que pueden evitar muchas asignaciones y, por lo tanto, el trabajo de recolección de basura.
  3. La implementación de más controles aumentará la complejidad del compilador y también la cantidad de tiempo necesario para la compilación. El primero de ellos ya es un problema; la interfaz del compilador no ha visto ningún cambio arquitectónico significativo en alrededor de una década. El trabajo actual de RakuAST que sienta las bases para las macros también implica una reescritura de la interfaz del compilador. La arquitectura mejorada debería facilitar la implementación de más comprobaciones de tipo en tiempo de compilación, pero también se está pensando en cómo se podrían paralelizar los aspectos de la compilación, lo que podría permitir al compilador hacer más sin aumentar el tiempo de compilación del reloj de pared.

Una vez que se completa la revisión actual de la interfaz del compilador, parece bastante probable que se introduzcan más comprobaciones en tiempo de compilación (pero solo se habiliten desde la siguiente versión de idioma), al menos, siempre que alguien trabaje en ello.

Sin embargo, se avecina una oportunidad aún más emocionante en esta área: dado que habrá una API para los programas de Raku, y con planes que se unirán para pases de compiladores personalizados, pronto también será posible implementar verificadores de tipos como módulos . Algunos de ellos pueden dar lugar a comprobaciones que se incluyan en futuras versiones del idioma Raku. Otros pueden ser bastante específicos de dominio y estar destinados a permitir un uso más correcto de un módulo determinado. Otros pueden imponer rigores que no están en el espíritu del idioma base, pero que algunos usuarios del idioma pueden optar por aceptar.