ūüáßūüá∑ Olhando para o futuro de Haskell e JavaScript para Plutus

(Originalmente publicado por Luite Stegeman em 04/06/2020, traduzido por Joselmo Cabral)

Olhando para o futuro de Haskell e JavaScript para Plutus

Contratos inteligentes usam o compilador cruzado GHCJS para traduzir código fora da cadeia

Esta é a segunda das postagens técnicas do desenvolvedor Deep Dive da nossa equipe Haskell. Esta série ocasional oferece uma visão sincera dos elementos centrais da plataforma e dos protocolos Cardano e fornece insights sobre as escolhas de engenharia que foram feitas. Aqui, descrevemos alguns dos trabalhos que estão sendo realizados para melhorar as bibliotecas e as ferramentas de desenvolvedor do Plutus, a plataforma de contratos inteligentes de Cardano.

Introdução

Na IOHK, estamos desenvolvendo a plataforma de contrato inteligente Plutus para o blockchain Cardano. Um contrato Plutus √© um programa Haskell parcialmente compilado no c√≥digo Plutus Core on-chain e parcialmente no off-chain. O c√≥digo on-chain √© executado pelos n√≥s da rede Cardano usando o int√©rprete do Plutus Core, a linguagem de contrato inteligente incorporada no ledger Cardano. √Č assim que a rede verifica as transa√ß√Ķes. O c√≥digo fora da cadeia √© para tarefas como a configura√ß√£o do contrato e a intera√ß√£o do usu√°rio. Ele √© executado dentro da carteira de cada usu√°rio do contrato, em um tempo de execu√ß√£o do node.js.

Compilar a parte fora da cadeia de um contrato Plutus envolve, portanto, traduzir o código fora da cadeia para JavaScript. Para isso, usamos o GHCJS, o compilador cruzado Glasgow Haskell to JavaScript.

No ano passado, n√£o fizemos muitas altera√ß√Ķes no gerador de c√≥digo GHCJS. Em vez disso, fizemos uma reestrutura√ß√£o para tornar as coisas de compila√ß√£o com o GHCJS mais confi√°veis ‚Äč‚Äče previs√≠veis, al√©m de adicionar suporte ao Windows e usar os recursos mais recentes do Cabal. Este post fornece uma vis√£o geral do que aconteceu e uma breve descri√ß√£o do que est√° reservado para este ano.

Suporte do Cabal

Ao instalar um pacote com o GHCJS, voc√™ provavelmente usa o sinalizador da linha de comando ‚Äďghcjs ou inclui o compilador: ghcjs no seu arquivo de configura√ß√£o. Isso ativa o flavor do compilador ghcjs no Cabal. O flavor ghcjs √© baseado no flavor ghc e adiciona recursos como suporte para a execu√ß√£o de scripts de configura√ß√£o criados pelo GHCJS e para adicionar fontes JavaScript.

A Cabal introduziu muitos recursos nos √ļltimos anos, incluindo suporte para mochila, compila√ß√Ķes locais no estilo Nix, v√°rias bibliotecas (nomeadas) por pacote e planos de compila√ß√£o por componente. Infelizmente, os novos recursos resultaram em muitas altera√ß√Ķes na base de c√≥digo e a manuten√ß√£o do flavor ghcjs ficou para tr√°s por algum tempo. Atualizamos o suporte ao GHCJS novamente na vers√£o 3.0. Se voc√™ deseja usar os recursos de constru√ß√£o de novo estilo, certifique-se de usar o cabal-install vers√£o 3 ou posterior.

As diferen√ßas entre os flavours do compilador ghcjs e ghc s√£o pequenas e o suporte √† compila√ß√£o cruzada no Cabal vem melhorando. Portanto, esperamos que, eventualmente, possamos descartar completamente o flavor do compilador ghcjs. As extens√Ķes seriam adicionadas como comportamento espec√≠fico da plataforma no flavor ghc.

Plug-ins do compilador

O GHC permite que o compilador seja estendido com plug-ins, o que pode alterar aspectos do pipeline de compilação. Por exemplo, os plug-ins podem introduzir novos recursos de otimização ou estender o typechecker.

Diferentemente do Template Haskell, que √© separado do compilador por meio da abstra√ß√£o Quasi typeclass, os plug-ins podem usar diretamente toda a API do GHC. Isso torna a abordagem de ‚Äúint√©rprete externo‚ÄĚ que o GHCJS introduziu para executar o Template Haskell em um compilador cruzado inadequado para plug-ins de compilador. Em vez disso, os plug-ins precisam ser criados para a plataforma de compila√ß√£o (que executa o GHC).

Em 2016, o GHCJS introduziu suporte experimental para plug-ins de compilador. Isso dependia de procurar o plug-in no banco de dados do pacote GHCJS e tentar encontrar uma correspond√™ncia pr√≥xima para o pacote e o m√≥dulo do plug-in no banco de dados do pacote GHC. Agora adicionamos um novo sinalizador para apontar o GHCJS para pacotes execut√°veis ‚Äč‚Äčno sistema de compila√ß√£o. Isso torna os plug-ins utiliz√°veis ‚Äč‚Äčnovamente com compila√ß√Ķes de novo estilo e outras configura√ß√Ķes de banco de dados de pacotes ‚Äėex√≥ticos‚Äô.

Em princípio, nosso novo sinalizador pode fazer com que os plug-ins funcionem em qualquer compilador cruzado do GHC, mas o requisito de também criar o plug-in com o compilador cruzado é bastante feio. Estamos trabalhando para remover esse requisito, seguido pela mesclagem do suporte de plug-in para compiladores cruzados no GHC upstream (consulte o ticket 14335 e o 17957).

Emscripten toolchain

Há muito, muito tempo, o GHCJS trabalhou no Windows. Uma ou duas almas corajosas podem realmente ter usado isso! Seus pacotes de inicialização (os pacotes criados pelo ghcjs-boot) incluem o pacote Win32 na plataforma de compilação do Windows. O motivo disso foi a configuração do Cabal com o GHCJS. O sinalizador de sistema operacional da Cabal (win32) seria definido se a plataforma de compilação fosse o Windows. Na época, era mais fácil corrigir o pacote para compilar sem erros com o GHCJS e incluí-lo nos pacotes de inicialização. No entanto, o pacote Win32 realmente não funcionou e mantê-lo atualizado era um fardo de manutenção. Em algum momento, ficou para trás e o GHCJS não funcionou mais no Windows.

Os pacotes de inicialização que precisam incluir o Win32 no Windows eram indicativos de uma separação ruim entre a plataforma de compilação (que executa o compilador) e a plataforma host (que executa o executável produzido pelo compilador). Isso foi causado pela falta de uma cadeia de ferramentas C completa para o GHCJS. Muitos pacotes não possuem apenas código Haskell, mas também arquivos produzidos por uma cadeia de ferramentas C, por exemplo, por meio de um script de configuração do Autotools ou hsc2hs.

A abordagem do GHCJS era incluir alguns arquivos pré-gerados e usar a cadeia de ferramentas C da plataforma de construção (a mesma que o GHC usaria) para todo o resto, esperando que não quebrasse. Se ele quebrasse, corrigiríamos o pacote.

Nos √ļltimos anos, o navegador como alvo de compila√ß√£o vem ganhando cada vez mais for√ßa. A Emscripten fornece uma cadeia de ferramentas C h√° muitos anos e recentemente mudou de seu pr√≥prio back-end do compilador para o compilador Clang com o back-end padr√£o do LLVM.

O Clang é suportado pelo GHC como uma cadeia de ferramentas C por um tempo. Ele pode gerar código asm.js e WebAssembly que podem ser executados diretamente no navegador. Infelizmente, os usuários do compilador ainda não podem interagir diretamente com o código C compilado por meio do C FFI (importação estrangeira chamada) no GHCJS. Mas ter uma cadeia de ferramentas C permite que pacotes que dependem de scripts de configuração ou hsc2hs sejam compilados com muito mais confiabilidade. Isso corrige alguns problemas de compilação de longa data e nos permite dar suporte ao Windows novamente. Achamos que isso já vale a dependência adicional.

Uma variante do GHCJS 8.6 usando a cadeia de ferramentas Emscripten está disponível na ramificação ghc-8.6-emscripten, que pode ser instalada no Windows. Desta vez, o conjunto de pacotes de inicialização é o mesmo em todas as plataformas de compilação. O Emscripten está planejado para ser o conjunto de ferramentas padrão do GHCJS 8.8 em diante.

Organização do código

Em algum momento nos primeiros dias, o GHCJS foi implementado como um patch para o GHC. Gerou JavaScript a partir do código intermediário STG que uma instalação regular do GHC produziu. Isso facilitou a instalação de pacotes com o GHCJS. Basta instalar normalmente e obter JavaScript gratuitamente. Mesmo o Template Haskell funcionou!

A desvantagem dessa abordagem foi que a plataforma de construção afetou muito o código gerado. Se você construir em uma máquina Linux de 64 bits, todas as constantes da plataforma virão da plataforma Linux. E o código seria construído com a suposição de que Int é de 64 bits. Isso não é muito eficiente para implementar em JavaScript. E Cabal não estaria ciente de que o JavaScript estava sendo gerado.

Mais tarde, passamos a usar o ghc como uma biblioteca, introduzindo o Hooks para alterar o pipeline de compilação, quando necessário. Isso tornou possível tornar o tamanho da palavra da plataforma GHCJS independente da plataforma de construção, introduzir a extensão JavaScriptFFI e executar o Template Haskell em um processo separado com o node.js.

Infelizmente, acabou sendo difícil acompanhar as mudanças na biblioteca ghc upstream. Além disso, a modificação do ghc existente por meio de Hooks incentivou os engenheiros a contornar problemas em vez de corrigi-los diretamente no upstream.

No início de 2018, decidimos criar uma biblioteca ghc personalizada para o GHCJS, instalada como ghc-api-ghcjs, permitindo que resolvamos problemas sérios antes que eles fossem mesclados no upstream. Recentemente, descartamos a biblioteca separada e construímos o código-fonte GHC e GHCJS em uma biblioteca: ghcjs.

Embora ainda não possamos construir o GHCJS com o sistema de criação do GHC, estamos usando a árvore de origem do GHC upstream muito mais diretamente novamente. Estamos voltando ao passado? Talvez, mas desta vez tenhamos nossa própria plataforma com uma cadeia de ferramentas e ferramentas de construção, evitando as armadilhas que tornaram essa abordagem tão problemática na primeira vez.

Outlook

Em 2019, vimos melhorias para fazer o Cabal funcionar novamente, trazer de volta o suporte ao Windows e melhorar a cadeia de ferramentas C. Esperamos que as altera√ß√Ķes subjacentes na base de c√≥digo do GHCJS tornem mais f√°cil mesclar mais upstream e tornar as bibliotecas compat√≠veis com menos altera√ß√Ķes. Continuando esse caminho de transformar o GHCJS em um compilador cruzado adequado, com uma cadeia de ferramentas adequada, al√©m de reduzir suas ferramentas personalizadas, pretendemos alinhar ainda mais e, eventualmente, mesclar o GHCJS ao GHC. Embora sempre existam bibliotecas espec√≠ficas da plataforma que simplesmente n√£o podem ser compiladas de maneira cruzada, nosso objetivo final √© fornecer ao GHC um destino JavaScript, juntamente com seus destinos x86_64, Arm, AArch64 e outros destinos de gera√ß√£o de c√≥digo.

1 Like