24 dias de Hackage 2015 - dia 12 - json-autotype: Inferindo tipos a partir de dados
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 12
Hoje, revisitamos um problema do
dia 4,
onde nós pegamos um JSON da Web para extrair informações sobre ele. Eu
mencionei então que estavámos usando uma representação não tipada para o
JSON. Idealmente, se não estamos sob muita pressão para realizar a tarefa,
queremos ter uma representação tipada dos dados, para evitar erros comuns como
tentar acessar um campo que não existe. A biblioteca Aeson
nos permite
escrever nossas próprias estruturas de dados e as converter de e para JSON. Se
estivermos sob controle dos dados e do modelo de dados, esse é o caminho que
queremos seguir.
E se, por qualquer razão, nós não já tivermos um modelo de dados tipado, mas, por exemplo, estivermos consumindo JSON de uma fonte que não nos deu uma especificação das estruturas de dados? (Talvez isso pudesse ser feito com JSON Schemas). Então, precisaríamos fazer engenharia reversa dos tipos de dados, possivelmente a partir de uma especificação informal das estruturas (o que é algo problemático).
Ou poderíamos tomar a rota preguiçosa e interir tipos plausíveis a partir de
alguma amostra representativa dos dados. Isso é o que o útil pacote
json-autotype
faz. Sua
documentação está na
sua página do GitHub
Gerando um módulo de tipos a partir de dados JSON
Eu salvei um pequeno documento JSON,
pittsburgh-code-and-supply-events.json
de uma query para a API de eventos do
Meetup.
A forma mais fácil de usar o json-autotype
é usar sua ferramenta de linha de
comando, a instalando globalmente antes de qualquer outra coisa.
Para nosso exemplo rápido, eu só gerei um pouco de Haskell manualmente (idealmente, isso faz parte de um processo de compilação automatizado):
Note que, apesar de que para esse exemplo nós só rodamos a inferência em um único documento JSON, podemos a rodar em vários documentos e receber tipos mais precisos.
Um olhar de relance no código gerado
Aqui está um exemplo do que foi gerado (reformatado e editado por clareza):
Não é muito melhor do que inferir e escrever o boilerplate na mão? Podemos “parsear” JSON diretamente para nosso conjunto de tipos.
O operador engraçadinho :|:
é um construtor de um tipo parecido com o
Either
usado para lidar com o fato de que, quando um campo está faltando em
um dos dados de exemplo, nós não podemos inferir se ele pode potencialmente ser
um objeto complexo que nós simplesmente desconhecemos na nossa amostra.
Usando os tipos gerados
Algums imports:
Nosso substituto para o getMeetupEventInfos
:
Note imediatamente as diferenças com a versão anterior. Nós estamos usando
Aeson.eitherDecode
para transformar bytes de JSON em um tipo completo
Meetup.TopLevel
e, portanto, podemos detectar de cara se o JSON que recebemos
era válido (no sentido de ter a mesma estrutura dos tipos que inferimos e
conhecemos).
Extraindo eventos:
Isso também é diferente do que tínhamos antes, já que não estamos só usando
strings para (se tudo der certo) encontrar os campos que queremos no JSON. Nós
temos uma estrutura de dados Meetup.TopLevel
e só usamos o acesso a
propriedades normal para records e mergulhamos nos dados.
Extraindo informação de um só evento:
De novo, nós só usamos os campos tipados de Meetup.ResultsElt
, ao invés de
strings como chaves em um objeto.
Finalmente, indo até o talo tentando extrair o nome do local a partir do objeto do local de um evento:
Aqui nós percebemos que talvez não haja nenhum local ou que o campo de JSON
venue
não corresponda ao tipo de dados Venue
. Na prática, reconhecer esse
fato pode resultar em nós mudarmos o nosso tipo EventInfo
para que ele não
assuma que tem um Text
como seu venueName
, mas aqui e agora nós só ficamos
no mundo “tipado por strings” e tentamos retornar uma string útil em todos os
casos.
De qualquer forma, você pode ver como ter uma estrutura de dados definida para
um JSON pode nos levar a repensar o que pressupomos quando desenhamos nosso
tipo EventInfo
. Talvez até melhorar nosso desenho. O que é legal no
json-autotype
é que você pode o usar sem ter que escrever os tipos para o
JSON na mão.
Provedores de tipos
A linguagem F# provem suporte para provedores de tipos, que são formas de conseguir tipos de algum lugar sem ter que os escrever você mesmo. Isso é uma funcionalidade muito legal que eu gostaria de ver em ecossistemas de linguagens mais tipadas. Por exemplo, existe uma biblioteca de provedores de tipos para Idris.
Eu desconheço quanto trabalho existe em um ecossistema de provedores de tipos
para Haskell, mas eu imagino que você poderia usar Template Haskell para, por
exemplo, automatizar um pouco do que fizemos aqui com o json-autotype
como
uma forma crua de um provedor de tipos.
Conclusão
json-autotype
é uma boa biblioteca que ajuda em entender o JSON que aparece
no seu caminho, mas sem um conjunto de tipos já especificado. Ele infere os
tipos e escreve um módulo de Haskell automaticamete.
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.
Há um milestone no GitHub com tarefas esperando por você.
Não quer traduzir posts? Escreva código que impacta o ecossistema de Haskell conosco