24 dias de Hackage 2015 - dia 13 - hint: Avaliação em runtime para Haskell
por Franklin Chen
traduzido por Pedro Yamada
Esse é um artigo escrito por Franklin Chen e traduzido para o português. Ler original.
Índice de toda a série
O índice de toda a série está no topo do artigo para o dia 1.
Dia 13
Um característica marcante de uma uma “linguagem dinâmica” como Lisp e JavaScript é a habilidade de avaliar código em runtime, dentro de um processo existente. Já que o runtime de carregar classes é uma funcionalidade fundamental do Java, Java também seria uma “linguagem dinâmica”? Acho que os termos “linguagem estática” e “linguagem dinâmica” não são muito úteis, e uma comparação de linguagens, compiladores e ambientes de desenvolvimento deveria se concentrar em funções especificas da experiência de usuário e onde as barreiras estão na semântica. Uma coisa complicada é que muito do que é interessante na verdade depende da implementação.
Por exemplo, o padrão do Haskell não diz nada sobre eval
em runtime, então há
algum sentido em que Haskell considerado como uma “linguagem” estritamente
definida não tem suporte nenhum para isso. Mas se considerarmos a implementação
e ecossistema do GHC, que é dominante hoje apesar da existência de outras
implementações do Haskell, existe muitas ferramentas que são “dinâmicas”, no
sentido de serem capazes de acessar APIs do GHC de uma ou muitas formas
diferentes.
Edward Yang recentemente escreveu um blog post interessante “The convergence of compilers, build systems and package managers” [N.T. A convergência de compiladores, sistemas de compilação e gerenciadores de pacotes] sobre um subset do problema genérico de o que pode ter acesso ao quê no contexto das ferramentas. Ele não tocou em avaliação em runtime, que é um tópico inteiro por si só.
Para hoje, decidi mencionar que você já pode fazer avaliação em runtime do
código Haskell no GHC usando o pacote
hint
e oferecer a ideia de que talvez pudesse ser útil ter algo menos ad-hoc do
que pacotes de terceiros como esse.
Por que carregar e avaliar Haskell dinamicamente?
Para mim, sempre se resume a ficar com inveja do mundo do Lisp.
Há momentos em que quis poder fazer avaliação e carregamento dinâmico de Haskell e desejei que estivesse trabalhando com Lisp. O maior exemplo é tentar suportar “plugins” escritos pelo usuário que podem ser carregados de um arquivo ou digitados em um REPL customizado. A forma mais limpa de fazer isso é escrever uma linguagem de domínio específico limitada e um parser, type checker (se a DSL tiver tipos) e compilador/interpretador para ela. Mas por que fazer tudo isso, se podemos só permitir que todo o poder do Haskell seja usado?
Felizmente, encontrei bibliotecas como o hint
que me permitiram fazer o que
queria. Vou mostrar um exemplo do tipo de coisas que tenho feito.
A tarefa
Imagine um programa que faz sorting (ordena coisas), e permite que o usuário,
durante runtime, ofereça uma função customizada para fazer isso ao invés da
padrão. Por exemplo, o usuário poderia ter especificado o path de um arquivo
Haskell OurSorter.hs
como um argumento da linha de comando ou o programa
poderia ter uma janela de preferências permitindo que o usuário digitasse o
texto de uma função de ordenamento.
Para fazer as coisas ainda mais interessantes, vamos dizer que a função de ordenação que vai ser especificada deve ser polimórfica, restrita apenas o suficiente para requerir a operação de comparação:
Como nós carregamos esse tipo de função em runtime?
Um wrapper de tipos necessário
A primeira coisa a tirar do caminha é que nós não podemos carregar uma função
com tipo Ord a => [a] -> [a]
diretamente, por causa da falta de suporte no GHC para
impredicate types.
Mesmo assim, há uma forma de contornar isso, que é amarrar o tipo dentro de um
newtype
, junto com o uso da funcionalidade de tipos de alta ordem
(higher-rank types). Eu aprendi esse macete lendo esse
artigo sobre impredicative types.
Agora nós podemos tentar carregar valores do tipo Sort
ao invés do tipo
Ord a => [a] -> [a]
.
Como carregar?
Vamos criar uma API chamada loadSort
que nos permite carregar um Sort
pesquisando por módulo e nome. Aqui está um teste do HSpec que ilustra que
queremos conseguir carregar um Sort
e o usar em tipos de listas
diferentes. Nós estamos usando Language.Haskell.Interpreter
para fazer o
trabalho:
Um “plugin” de exemplo
Nós criamos um “plugin” de exemplo em um diretório cujo código não é compilado junto com o programa principal. Imagine que o usuário tem um diretório separado de plugins.
O carregador
A parte mais interessante é o uso da função interpret
que tem tipo
recebendo uma string e uma “testemunha” para um tipo monomórfico para que o
interpret
saiba qual dicionário em runtime usar para o Typeable
(a forma
padrão moderna para uma biblioteca fazer isso seria ter usado uma
Proxy
).
Então aqui está: avaliação em runtime no Haskell do GHC. O que o hint
nos dá
é razoavelmente primitivo, mas eu achei útil.
Conclusão
Eu gostaria de ver mais suporte oficial para o dinamismo em ambientes de
linguagens como Haskell. Isso requere acesso às entranhas do compilador ou APIs
oficiais, mas acho que é o caminho a ser seguido. Separações de fase com
princípios são importantes, como é a integração entre elas. Eu gosto que o
hint
existe para permitir que eu carregue código Haskell dinamicamente.
Todo o código
Todo o código para a série estará nesse repositório do GitHub.
Nota do tradutor
Se você quer ajudar com esse tipo de coisa, agora é a hora. Entre no
Slack ou no
IRC da HaskellBR e
contribua. Esse blog e outros projetos associados estão na
organização haskellbr
no GitHub e em
haskellbr.com/git.