24 dias de Hackage, 2015 - dia 4 - wreq: Programação de clientes Web; com notas sobre lens e a sintaxe de operadores
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 4
No final dos anos 90, eu entusiasmadamente comprei o livro “Web Client Programming with Perl” [N.T. “Programação de Clientes Web com Perl” e usei a biblioteca LWP para fazer Web scraping automatizado. Eu continuei fazendo isso anos 2000 adentro. Estou feliz de que hoje, eu posso só usar Haskell para fazer esse tipo de programação e o fazer de forma sucinta.
O tópico de hoje é o wreq
, a biblioteca de
alto nível de Bryan O’Sullivan para fazer
programação de clientes Web e desenhada com usabilidade em mente.
[N.T. Pense requests
para Python ou superagent
para Node.js]
wreq
usa o ecossistema do aeson
para JSON e lens
e seu
ecossistema, incluindo
lens-aeson
, então você pode
querer conferir os posts dos 24 dias de Hackage de 2012 pelo Ollie sobre o
aeson
e lens.
Já que o wreq
já tem um
tutorial e documentação extensivos, eu não
vou repetir suas explicações. Ao invés disso, vou dar um exemplo de uso que deve
ser simples o suficiente para ser entendido dentro do contexto e discutir os
problemas de usar a sintaxe de operadores em Haskell.
A tarefa
Sou um membro de muitos grupos no Meetup. É frequentemente útil para mim de conseguir informação usando a API oficial do Meetup ao invés de ficar clicando em um site ou um app. Por que na mão o que posso fazer corretamente e eficientemente com código?
Aqui está um exemplo muito simplificado de algo que eu posso querer fazer com o Meetup. Eu sou ativo na comunidade Pittsburgh Code and Supply, que tem uma página no Meetup com uma agenda cheia de eventos (está em hiato agora em Dezembro para as festas, mas em geral é muito ativa). Talvez eu queira descobrir quais são os próximos eventos e buscar por eventos que são interessantes de acordo com algum critério. Para nosso exemplo, vamos dizer que eu quero encontrar os dez próximos eventos, seus nomes e locais, e ter certeza de que pelo menos um evento que tem um nome e local já configurados (as vezes um evento já foi proposto, mas ainda não tem um local).
Um teste
Ontem, dia 3 dessa série de artigos, mencionei gostar de usar o HSpec, então vamos usar o HSpec.
Estamos usando o tipo “empacotado” Unicode de strings
text
porque é isso que o wreq
usa. OverloadedStrings
é uma extensão do GHC
conveniente que permite literais de string no código serem tratados como Text
ao invés de String
. Ollie discutiu essa extensão no seu Dias de Extensões do
GHC 2014.
(Nota em português sobre Text e OverloadedStrings)
Also, since I’m operating in test-driven development style, I wrote
this test first, before writing the WreqExample
module: I only wrote
the imports for what I need for the test.
Assinaturas para Módulos
Se Haskell tivesse assinaturas para módulos, como Standard ML e OCaml, eu escreveria uma assinatura explícita para o módulo que eu pretendo implementar que vai conformar com ela, mas Haskell não as tem, então o melhor que podemos fazer é operar à moda do “duck typing” no nível dos módulos, confiando implicitamente que compilação falhe em um import de uma implementação ao invés de usar uma assinatura explícita sem implementação.
Aqui estão os tipos que precisamos (em pseudo-código fingindo que Haskell tivesse assinaturas para módulos):
Implementação
Imports
Tipos
A parte do cliente Web
Já que só estamos fazendo um request, e não estamos lidando com nenhum erro,
mas deixando o wreq
atirar exceções, a parte do cliente Web é muito curta. A
API do Meetup pode retornar informação como JSON.
Nós fazemos um GET
com alguns parâmetros QueryString. wreq
usa “lens” como
sua DSL para criar opções para o GET
, então vamos criar um valor Options
do
wreq
, configurando parâmetros um depois do outro usando o “builder pattern” e
começando com o defaults
:
Então fazemos o request e conseguimos uma resposta, que é uma
ByteString
lazy:
A parte do JSON
Então precisamos parsear essa resposta ByteString
lazy, incluindo os headers,
em um objeto JSON, um Value
do aeson
:
O tipo Value
é um ADT com muitas clausulas:
A parte lens
Foi irritante descobrir a partir da API oficial do Meetup quais campos eu precisava da resposta e quais eram seus tipos. Na prática, eu só salved o JSON de um request que sabia que faria e olhei para os campos que queria. Me disseram onde encontrar a documentação gerada de todos os métodos da API, mas ela não era ideal. Um outro dia de Hackage vai discutir o que fiz quanto a esse problema.
Nós extraímos a lista de eventos, usando uma “traversal” para extrair toda a lista, que está codificada como um array JSON no campo “results” da resposta:
Aqui nós usamos toListOf
do lens com uma “traversal” e um objeto JSON para
extrair o que precisamos.
Finalmente, já que nós só queremos, para cada evento, seu nome e seu local:
Usamos lens de novo, no nível de cada objeto de evento, para extrair o que queremos deles:
Aqui usamos a função view
do lens
, para aplicar uma lens no objeto JSON e
extrair um campo dele.
E acabamos! Escrevemos um script que parece mais ou menos o que você escreveria em Python ou Perl. Ele também vai “falhar” de formas similares, porque nós não estamos usando muitos tipos; mesmo o resultado final só tem strings, que podem ser vazias, por exemplo, o que quer que isso possa significar. Por exemplo, se você tentar encontrar um campo que não existe, o código aqui vai só dar uma string vazia. Podemos fazer melhor? Sim, de muitas maneiras. Fique ligado para outro dia de Hackage.
Sintaxe de operadores lens
Se você já usou o wreq
ou o lens
, você deve ter percebido algo estranho: eu
não usei nenhum operador do lens
. Isso foi proposital. Apesar de que o
tutorial do wreq
dá
um pouco de background para o lens,
a realidade é que quando pessoas que não são experientes com lens ou Haskellers
me perguntaram como programar clientes Web em Haskell, e eu disse que wreq
era uma boa escolha, eles travaram imediatamente na parte de lens. Olhando para
o tutorial, eu vejo que ele pula diretamente para uma sopa de operadores. É uma
pena. Você pode usar bibliotecas como o wreq
, sem ter os operadores do lens
memorizados. Você tem que entender alguns fatos (como o uso de composição de
funções para compor lenses) e ter uma ideia de como os tipos funcionam, mas
uma coisa da qual você não precisa são os operadores engraçadinhos. Eu acho que
é melhor entender como fazer as coisas sem operadores antes de começar a os
usar.
Por exemplo, uma forma idiomática de criar o objeto de opções, como apresentado
na seção “whirlwind tour” do tutorial do wreq
, é:
Eu não gosto da ideia de recem-chegados à biblioteca copiarem e colarem coisas sem entender o que elas fazem, ou ter a ideia que esses operadores fazem parte da linguagem Haskell ou que eles são um requisito para usar a biblioteca. As pessoas têm essas impressões.
Eu gosto muito do operador de funções reverso &
, apesar de que ele não é tão
sugestivo quanto o mesmo operador em outras linguagens (como F#, OCaml, Elm,
Elixir) que usam um pipe
|>
,
então não me importo de o usar.
Mas não acho o .~
muito sugestivo para recem-chegados para o lens
. Será que
set lens newValue object
é tão pior de ler e escrever que object & lens .~
newValue
?
Code golf?
Para ilustrar tanto os pros e contras de usar operadores (mas nesse caso principalmente contras, eu acho), aqui está uma versão “code golf” de todo o código:
De certa forma, isso parece legal porque o código com pipes à direita e à esquerda é fácil e natural de ler; isso é, se você sabe todos os operadores e gosta de combinadores point-free e secção de operadores. Mas quando eu mostrei isso para meus amigos que não são tão fluentes em Haskell, eles não gostaram muito. Note que eu fiz concessões para construir essa pipeline. Eu perdi comentários, os procedimentos nomeados e até meu tipo definido do resultado. Eu acho que algo foi perdido por escrever as coisas nesse estilo, ainda que alguma parte de mim goste dele.
Uma entrevista com Bryan O’Sullivan
Recentemente (Setembro de 2015), o Haskell Cast
entrevistou Bryan O’Sullivan. Eu recomendo escutar todo o episódio.
Ele tinha histórias para contar sobre como ele começou com Haskell, como ele
acabou escrevendo todas essas bibliotecas e como ele desenha elas e quais são
suas metas enquando as implementa. Note que aeson
e text
, que todo mundo
usa, são suas criações. Obrigado, Bryan, por tudo que fez para a comunidade
Haskell!
Recursos sobre lens
Gabriel Gonzalez escreveu um tutorial sobre lens que é útil. Obrigado, Gabriel, por escrever tutorials não só para suas próprias bibliotecas, mas também para outras!
Conclusão
Para o dia 4, eu apresentei um pequeno exemplo que usa wreq
com aeson
e
lens
para realizar uma tarefa simples de extrair informação da Web e tentei
fazer o wreq
um pouco mais acessível sem requerir o uso dos operadores lens
de cara.
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.