BookinglyTech News
Software

Metacarp, un segundo compilador de Carp escrito en Carp y con JIT incremental

Metacarp, una implementación alternativa de Carp escrita en Carp, compila el conjunto de referencia, se compila a sí misma y añade sesiones persistentes y JIT incremental. Aún no es reemplazo directo.

3 min de lecturaLobsters0 vistas

Metacarp es un segundo compilador para Carp. El propio Metacarp está escrito en Carp, compila Carp y ya es capaz de compilar el conjunto de referencia, compilarse a sí mismo y volver a compilarse hasta obtener C idéntico byte a byte. No es un reemplazo directo del compilador de referencia en Haskell, que lleva alrededor de una década en uso, y su autor no lo presenta como tal.

El binario se construye con el compilador de referencia: carp -b --optimize main.carp produce out/carp-compiler. A partir de ahí se puede invocar sobre un programa Carp para generar una unidad de traducción C, o pedir que use Clang y ejecute el resultado. En el ejemplo del proyecto, un programa que carga la librería estándar, expande macros, infiere y especializa, comprueba propiedad, emite C y lo compila imprime 220: la suma de los cuadrados de los números pares del uno al diez. Es mucha maquinaria para ese resultado, pero sirve para enseñar la cadena completa.

Metacarp entiende la parte incómoda de implementar Carp: lenguaje de compilación y macros, inferencia de tipos Hindley-Milner, interfaces, monomorfización, coincidencia de patrones, clausuras y las reglas de propiedad y préstamos. Deriva las funciones de copia y borrado que necesitan los valores gestionados y las inserta antes de producir C. Carga Core y compila programas existentes; también funciona con las librerías de Carpentry. La compatibilidad no es del 100%, y los fallos que quedan aparecen en fases tardías.

El compilador como librerías

El proyecto son unas 42.000 líneas de Carp repartidas en librerías que implementan fases separadas: registro de fuentes, carga de módulos, parseo de superficie, expansión de macros, resolución de nombres, inferencia de tipos, especialización, planificación de propiedad, bajada al backend y C. No son módulos internos: cada frontera tiene sus propios modelos de datos, puntos de entrada, pruebas y errores. Se puede usar el parser sin el comprobador de tipos, la inferencia sin backend, o la planificación de propiedad sin LLVM ni C. Los fallos de parseo devuelven rangos del código fuente, y solo el programa de línea de comandos los convierte en salida de terminal.

Sesiones que no se tiran

La librería carp-session mantiene un compilador vivo. Al crear una sesión se cargan, expanden, resuelven, derivan e infieren Core una vez. Sobre esa base inmutable se guardan definiciones aportadas por el cliente. Una celda de cuaderno se comprueba de forma transitoria contra Core más las definiciones confirmadas más la propia celda, sin formar parte de la sesión. El cliente puede confirmar, reemplazar o eliminar definiciones; cuando una cambia, la sesión encuentra las dependientes y reconstruye la vista afectada. Si el cambio falla, el estado candidato se descarta y queda el último bueno. Eso da el comportamiento esperado en un cuaderno: una celda define (defn twice [x] (* x 2)) y otra pide su tipo, información de autocompletado o compila (twice 21) sin pegar la primera delante ni recargar la librería estándar. La API responde con definiciones, tipos, diagnósticos, documentación, autocompletado, expansiones y rangos relativos al fuente, y ofrece puntos de enganche para parar tras inferencia, tras propiedad o tras bajada. Es la maquinaria que hay detrás de gt4carp, donde una página de Lepiter tiene una sesión caliente y los fragmentos se comportan como partes de un programa en evolución.

La compilación con estado suena obvia, pero implementarla no lo fue. La compilación por lotes infiere un programa cerrado, usa el estado del solucionador y lo tira. Una sesión persistente combina hechos de distintas ejecuciones y los reutiliza entre generaciones. En un momento funcionó hasta que una celda posterior hizo alcanzable una definición polimórfica que antes no se usaba; entonces la especialización encontró una variable de tipo cuya sustitución había desaparecido varias peticiones atrás. El primer arreglo correcto afectó al tiempo de compilar una celda contra el propio Metacarp, según cuenta el autor. Metacarp no está listo para reemplazar al compilador de Haskell, pero ya enseña una arquitectura distinta: fases como librerías, backend LLVM alternativo y JIT incremental. Quien siga Carp tiene ahora una segunda implementación que, por lo menos, compila el lenguaje y se compila a sí misma.