Traducción al español de “Looking to the future of Haskell and JavaScript for Plutus”
Publicado por Luite Stegeman en el blog de IOHK el 3 de Junio de 2020
Los contratos inteligentes usan el compilador cruzado GHCJS para traducir el código fuera de cadena
Este es el segundo de los posteos técnicos de inmersión profunda de nuestro equipo de Haskell. Esta serie ocasional ofrece un vistazo sincero a los elementos centrales de la plataforma y protocolos de Cardano, y da una idea de las elecciones de ingeniería que se han hecho. Aquí, esbozamos algunos de los trabajos que se han realizado para mejorar las bibliotecas y las herramientas de desarrollo Plutus, la plataforma de contratos inteligentes de Cardano.
Introducción
En IOHK estamos desarrollando la plataforma de contratos inteligentes Plutus para la blockchain Cardano. Un contrato Plutus es un programa de Haskell que está compilado en parte en código Plutus Core y en parte en código off-chain. El código en cadena es ejecutado por los nodos de la red Cardano usando el intérprete de Plutus Core, el lenguaje de contratos inteligentes incrustado en el libro contable de Cardano. Así es como la red verifica las transacciones. El código off-chain es para tareas como la configuración del contrato y para la interacción con el usuario. Se ejecuta dentro de la billetera de cada usuario del contrato, en un tiempo de ejecución node.js.
Compilar la parte off-chain de un contrato Plutus implica por lo tanto traducir el código off-chain a JavaScript. Para lograr esto, usamos GHCJS, el cross-compilador de Glasgow Haskell a JavaScript.
En el último año, no hemos hecho muchos cambios en el generador de código GHCJS. En su lugar, hicimos algunas reestructuraciones para que la compilación con GHCJS sea más fiable y predecible, así como añadir soporte para Windows y hacer uso de las más recientes características de Cabal. Este post da una visión general de lo que ha sucedido, y una breve mirada a lo que está en reserva para este año.
Soporte de Cabal
Cuando se instala un paquete con GHCJS, probablemente se utiliza la bandera de línea de comandos –ghcjs o se incluye el compilador: ghcjs en el archivo de configuración. Esto activa el sabor del compilador ghcjs en Cabal. El sabor de ghcjs se basa en el sabor de ghc y añade características como el soporte para ejecutar scripts de configuración construidos por GHCJS, y para añadir fuentes de JavaScript.
Cabal ha introducido muchas características en los últimos años, incluyendo soporte para mochila, construcciones locales estilo Nix, múltiples bibliotecas (con nombre) por paquete, y planes de construcción por componente. Desafortunadamente, las nuevas características resultaron en muchos cambios en la base de código, y el mantenimiento para el sabor de ghcjs quedó atrás por algún tiempo. Hemos actualizado el soporte de GHCJS de nuevo en la versión 3.0. Si quieres usar las nuevas características de construcción, asegúrate de usar la versión 3 o posterior de Cabal-install.
Las diferencias entre los sabores de los ghcjs y del compilador ghc son menores, y el soporte de compilación cruzada en Cabal ha ido mejorando. Por lo tanto, esperamos que eventualmente seamos capaces de dejar el sabor del compilador ghcjs por completo. Las extensiones se añadirían en su lugar como comportamiento específico de la plataforma en el sabor ghc.
Plug-ins del compilador
El GHC permite que el compilador se amplíe con plug-ins, que pueden cambiar aspectos de la tubería de compilación. Por ejemplo, los plug-ins pueden introducir nuevas características de optimización o ampliar el corrector tipográfico.
A diferencia de la plantilla Haskell, que está separada del compilador a través de la abstracción Quasi typeclass, los plug-ins pueden utilizar directamente toda la API del GHC. Esto hace que el enfoque de “intérprete externo” que GHCJS introdujo para ejecutar Template Haskell en un compilador cruzado no sea adecuado para los plug-ins del compilador. En su lugar, los plug-ins necesitan ser construidos para la plataforma de construcción (que ejecuta el GHC).
En 2016, GHCJS introdujo soporte experimental para los plug-ins del compilador. Esto se basó en la búsqueda del plug-in en la base de datos de paquetes del GHCJS y luego tratar de encontrar una coincidencia cercana para el paquete y el módulo del plug-in en la base de datos de paquetes del GHC. Ahora hemos añadido una nueva bandera para apuntar el GHCJS a los paquetes que se pueden ejecutar en el sistema de construcción. Esto hace que los plug-ins puedan ser utilizados de nuevo con nuevas construcciones y otras configuraciones de bases de datos de paquetes ‘exóticos’.
En principio, nuestra nueva bandera puede hacer que los plug-ins funcionen en cualquier cross-compilador GHC, pero el requisito de construir también el plug-in con el cross-compilador es bastante feo. Estamos trabajando en la eliminación de este requisito seguido de la fusión del soporte de plug-ins para cross-compiladores en el GHC anterior (ver ticket 14335 y ticket 17957)
Cadena de herramientas Emscripten
Hace mucho, mucho tiempo, GHCJS trabajaba en Windows. ¡Una o dos almas valientes podrían haberlo usado! Sus paquetes de arranque (los paquetes construidos por ghcjs-boot) incluirían el paquete Win32 en la plataforma de construcción de Windows. La razón de esto fue la configuración de Cabal con GHCJS. La bandera de Cabal os(win32) se pondría si la plataforma de construcción fuera Windows. En ese momento lo más fácil era simplemente parchear el paquete para construir sin errores con GHCJS, e incluirlo en los paquetes de arranque. Sin embargo, el paquete de Win32 no funcionaba realmente, y mantenerlo actualizado era una carga de mantenimiento. En algún momento se quedó atrás y GHCJS ya no funcionaba en Windows.
El hecho de que los paquetes de arranque tuvieran que incluir Win32 en Windows era indicativo de una mala separación entre la plataforma de construcción (que ejecuta el compilador) y la plataforma anfitriona (que ejecuta el ejecutable producido por el compilador). Esto fue causado por la falta de una cadena de herramientas C completa para GHCJS. Muchos paquetes no sólo tienen código Haskell, sino que también tienen archivos producidos por una cadena de herramientas C, por ejemplo a través de un script de configuración de Autotools o hsc2hs.
El enfoque de GHCJS fue incluir algunos archivos pre-generados, y usar la plataforma de construcción C toolchain (la misma que usaría GHC) para todo lo demás, esperando que no se rompiera. Si se rompía, parchearíamos el paquete.
En los últimos años, el navegador web como objetivo de compilación ha ido ganando cada vez más terreno. Emscripten ha estado proporcionando una cadena de herramientas C durante muchos años, y recientemente ha cambiado de su propio compilador de fondo al compilador Clang con el estándar de fondo LLVM.
Clang ha sido soportado por GHC como una cadena de herramientas C por un tiempo. Puede producir código asm.js y WebAssembly que puede ejecutarse directamente en el navegador. Desafortunadamente, los usuarios del compilador todavía no pueden interactuar directamente con el código C compilado a través del C FFI (foreign import ccall) en GHCJS. Pero tener una cadena de herramientas de C permite que los paquetes que dependen de scripts de configuración o hsc2hs compilen de manera mucho más confiable. Esto arregla algunos problemas de compilación de larga data y nos permite soportar Windows de nuevo. Pensamos que esto ya vale la pena la dependencia adicional.
Una variante de GHCJS 8.6 usando la cadena de herramientas Emscripten está disponible en la rama ghc-8.6-emscripten, que puede ser instalada en Windows. Esta vez, el conjunto de paquetes de arranque es el mismo en cada plataforma de construcción. Emscripten está planeado para ser la cadena de herramientas estándar en GHCJS 8.8 en adelante.
Organización del código
En algún momento de los primeros días, el GHCJS se implementó como un parche para el GHC. Generó JavaScript a partir del código intermedio del STG que una instalación regular de GHC produjo. Esto facilitó la instalación de paquetes con el GHCJS. Sólo tienes que instalar normalmente y obtener JavaScript de forma gratuita. ¡Incluso la plantilla Haskell funcionó!
La desventaja de este enfoque era que la plataforma de construcción afectaba mucho al código generado. Si construyes en una máquina Linux de 64 bits, todas las constantes de la plataforma vendrían de la plataforma Linux. Y el código se construiría asumiendo que Int es de 64 bits. Eso no es muy eficiente para implementarlo en JavaScript. Y Cabal no se daría cuenta de que se está generando Javascript en absoluto.
Más tarde, cambiamos a usar ghc como una biblioteca, introduciendo Hooks para cambiar la tubería de compilación donde fuera necesario. Esto hizo posible que la plataforma GHCJS fuera independiente de la plataforma de construcción, introducir la extensión JavaScriptFFI y ejecutar la plantilla Haskell en un proceso separado con node.js.
Desafortunadamente, resultó ser difícil mantenerse al día con los cambios en la biblioteca ghc aguas arriba. Además, la modificación del ghc existente a través de Hooks animó a los ingenieros a trabajar en torno a los problemas en lugar de arreglarlos directamente aguas arriba.
A principios de 2018, decidimos construir una biblioteca de ghc personalizada para GHCJS, instalada como ghc-api-ghcjs, lo que nos permitió trabajar en torno a problemas graves antes de que se fusionaran aguas arriba. Recientemente, dejamos de lado la biblioteca separada, y construimos tanto el código fuente de GHC como el de GHCJS en una sola biblioteca: ghcjs.
Aunque todavía no podemos construir el GHCJS con el sistema de construcción del GHC, estamos usando el árbol de fuentes del GHC de la corriente ascendente mucho más directamente de nuevo. ¿Vamos a volver al pasado? Tal vez, pero esta vez tenemos nuestra propia plataforma con una cadena de herramientas y herramientas de construcción, evitando los escollos que hicieron que este enfoque fuera tan problemático la primera vez.
Perspectiva
En 2019, vimos mejoras para hacer que Cabal funcione de nuevo, para traer de vuelta el soporte de Windows y para mejorar la cadena de herramientas C. Esperamos que los cambios subyacentes en la base de código de GHCJS faciliten la fusión de más arriba y hagan que las bibliotecas sean compatibles con menos cambios. Al continuar este camino de convertir el GHCJS en un compilador cruzado con una cadena de herramientas adecuada, así como al reducir sus herramientas personalizadas, pretendemos alinear aún más y eventualmente fusionar el GHCJS con el GHC. Aunque siempre habrá librerías específicas de la plataforma que simplemente no pueden ser compiladas de forma cruzada, nuestro objetivo final es proporcionar a GHC un objetivo de JavaScript, junto con sus objetivos de generación de código x86_64, Arm, AArch64, y otros.