Your Web News in One Place

Help Webnuz

Referal links:

Sign up for GreenGeeks web hosting
January 6, 2022 11:51 am GMT

Cdigo simples diferente de cdigo simplista: Elm vs JavaScript

Existem linguagens, frameworks e bibliotecas que se esforam para permitir que seja possvel realizar tarefas relativamente complexas escrevendo poucas linhas de cdigo. JavaScript um bom exemplo. Para fazer uma chamada http para uma pgina do meu site usando esta linguagem, basta escrever uma nica linha:

await fetch("https://segunda.tech/sobre")

A maioria das pessoas provavelmente no considera este cdigo difcil ou complexo, mas podem existir cenrios de erros escondidos que no so triviais de tratar. Para analisar isso, vou mostrar uma implementao de uma pequena pgina utilizando JavaScript puro e discutir os potencias problemas. Em seguida vou mostrar como implementar a mesma soluo utilizando a linguagem de programao Elm e analisar os mesmos pontos.

Exerccio: recuperando os nomes dos Pokmons

Para exemplificar o problema que quero discutir neste artigo, implementei em html e JavaScript puro (utilizando Ajax) o mnimo necessrio para exibir uma lista com nomes de Pokmons. Para isso utilizei a API do site PokAPI. O endpoint para recuperar a lista dos 5 primeiros Pokmons bem simples: basta acionar a URL https://pokeapi.co/api/v2/pokemon?limit=5 e o retorno ser um json contendo o resultado abaixo.

{  "count": 1118,  "next": "https://pokeapi.co/api/v2/pokemon?offset=5&limit=5",  "previous": null,  "results": [    {      "name": "bulbasaur",      "url": "https://pokeapi.co/api/v2/pokemon/1/"    },    {      "name": "ivysaur",      "url": "https://pokeapi.co/api/v2/pokemon/2/"    },    {      "name": "venusaur",      "url": "https://pokeapi.co/api/v2/pokemon/3/"    },    {      "name": "charmander",      "url": "https://pokeapi.co/api/v2/pokemon/4/"    },    {      "name": "charmeleon",      "url": "https://pokeapi.co/api/v2/pokemon/5/"    }  ]}

Neste exerccio o objetivo recuperar estes dados de forma assncrona e listar na pgina html apenas o contedo do campo name (que est dentro de result).

Implementando uma soluo utilizando html e JavaScript puro

Existem vrias formas de se resolver este problema utilizando estas tecnologias. Abaixo apresento a minha implementao.

<!doctype html><html lang="pt-BR"><head>  <meta charset="utf-8">  <title>Lista de Pokmons em HTML e JavaScript</title>  <meta name="author" content="Marcio Frayze David"></head><body>  <p id="loading-message">    Carregando lista de nomes dos Pokmons, aguarde...  </p>  <ul id="pokemon-names-list">  </ul>  <script>    (async function() {      await fetch("https://pokeapi.co/api/v2/pokemon?limit=5")        .then(data => data.json())        .then(dataJson => dataJson.results)        .then(results => results.map(pokemon => pokemon.name))        .then(names => addNamesToDOM(names))      hideLoadingMessage()    })();    function addNamesToDOM(names) {      let pokemonNamesListElement = document.getElementById('pokemon-names-list')      names.forEach(name => addNameToDOM(pokemonNamesListElement, name))    }    function addNameToDOM(pokemonNamesListElement, name) {      let newListElement = document.createElement('li')      newListElement.innerHTML = name      pokemonNamesListElement.append(newListElement)    }    function hideLoadingMessage() {      document.getElementById('loading-message').style.visibility = 'hidden'    }  </script></body></html>

A ideia que ao finalizar a chamada Ajax, a mensagem de carregamento deixe de aparecer e a lista contendo os nomes dos Pokmons seja carregada dentro da tag com o id pokemons-names-list. Publiquei esta pgina no editor on-line JSFiddle para que veja o comportamento esperado.

Sei que dificilmente algum escreveria um cdigo desta forma. No usei nenhum framework ou biblioteca externa e fiz algumas coisas que muitos considerariam ms prticas (como por exemplo colocar o cdigo JavaScript direto no html). Mas mesmo que tivesse implementado esta soluo com tecnologias populares como React, JSX e Axios, os potencias problemas que quero discutir aqui provavelmente ainda existiriam.

Analisando o cdigo acima, as perguntas que gostaria que voc tentasse responder so:

  • O que vai acontecer caso ocorra um timeout na chamada Ajax?
  • Se o servidor voltar um status http de falha, o que vai acontecer?
  • Se o servidor retornar um status http de sucesso mas o formato do contedo retornado for diferente do esperado, o que vai acontecer?

O cdigo acima no responde nenhuma destas perguntas de forma clara. fcil visualizar o "caminho feliz", mas qualquer situao inesperada no esta sendo tratada de forma explcita. E embora nunca devssemos colocar um cdigo em produo que no trate estes cenrios, a linguagem JavaScript no nos obriga a lidar com eles. Caso algum do seu time esquea de fazer o tratamento adequado para um desses potenciais problemas, o resultado ser um erro em tempo de execuo.

Se o seu time tiver azar, talvez estes cenrios s apaream quando o cdigo j estiver em produo. E quando isso inevitavelmente acontecer, bem provvel que coloquem a culpa na pessoa desenvolvedora que implementou aquela parte do sistema.

Mas se sabemos que este tipo de situao precisa obrigatoriamente ser tratada, por que as linguagens, frameworks e bibliotecas permitem que este tipo de cdigo seja escrito?

O que uma soluo simples?

Existe uma diferena grande entre uma soluo ser simples e ser simplista. Esta soluo que escrevi em JavaScript no simples, mas simplista, j que ela ignora aspectos fundamentais do problema em questo.

J linguagens como Elm, por sua vez, tendem a nos obrigar a pensar e implementar a soluo para todos os potenciais cenrios de problemas. O cdigo final provavelmente ser maior, mas trar consigo a garantia de que no vamos ter nenhum erro em tempo de execuo, j que o compilador verifica e impe que a pessoa desenvolvedora trate todos os caminhos possveis, no deixando espao para falhas previsveis.

Claro que isso no significa que webapps criadas nesta linguagem esto isentas de todo e qualquer tipo de erro. Podem ocorrer problemas na lgica de negcio e a aplicao ter um comportamento inesperado, ou aspectos visuais do sistema podem no estar como gostaramos. Mas aqueles erros previsveis, que podem ser encontrados por um compilador, vo deixar de existir. Um bom exemplo o famoso Undefined is not a function do JavaScript. J em Elm, impossvel escrever um cdigo que resulte em qualquer erro de runtime.

Outra vantagem desta abordagem que temos um cdigo realmente auto-documentado. Deve ficar muito claro, por exemplo, qual o formato do retorno esperado, quais campos so obrigatrios e quais so opcionais, etc.

Implementando a mesma soluo em Elm

Agora vamos analisar uma soluo escrita em Elm para este mesmo problema. Se voc no conhece essa linguagem (ou alguma outra similar, como Haskell ou PureScript), provavelmente vai achar a sua sintaxe um pouco estranha. Mas no se preocupe, voc no precisa entender totalmente este cdigo para compreender a proposta deste artigo.

Primeiro precisamos de um arquivo html simples, que ir hospedar nossa pgina. Esta abordagem bastante similar ao que feito quando utilizamos ferramentas como React ou Vue.

<!doctype html><html lang="pt-BR"><head>  <meta charset="utf-8">  <title>Lista de Pokmons em HTML e Elm</title>  <meta name="author" content="Marcio Frayze David"></head><body>  <main></main>  <script>    Elm.Main.init({ node: document.querySelector('main') })  </script></body></html>

Desta vez nosso html quase no tem lgica. Ele apenas ir carregar a aplicao escrita em Elm (previamente compilada) e colocar seu contedo dentro da tag main.

Agora sim a parte interessante: o cdigo escrito em Elm. Vou primeiro listar o cdigo por completo e depois destacar e comentar algumas partes mais relevantes para o tema deste artigo.

module Main exposing (..)import Browserimport Html exposing (..)import Httpimport Json.Decode exposing (Decoder)-- MAINmain =  Browser.element    { init = init    , update = update    , subscriptions = subscriptions    , view = view    }-- MODELtype alias PokemonInfo = { name : String }type Model  = Failure  | Loading  | Success (List PokemonInfo)init : () -> (Model, Cmd Msg)init _ =  (Loading, fetchPokemonNames)-- UPDATEtype Msg  = FetchedPokemonNames (Result Http.Error (List PokemonInfo))update : Msg -> Model -> (Model, Cmd Msg)update msg model =  case msg of    FetchedPokemonNames result ->      case result of        Ok pokemonsInfo ->          (Success pokemonsInfo, Cmd.none)        Err _ ->          (Failure, Cmd.none)-- SUBSCRIPTIONSsubscriptions : Model -> Sub Msgsubscriptions model =  Sub.none-- VIEWview : Model -> Html Msgview model =  case model of    Failure ->      text "Por alguma razo, no foi possvel carregar a lista com nome dos Pokmons. "    Loading ->      text "Carregando lista de nomes dos Pokmons, aguarde..."    Success pokemonsInfo ->      ul []        (List.map viewPokemonInfo pokemonsInfo) viewPokemonInfo : PokemonInfo -> Html MsgviewPokemonInfo pokemonInfo =  li [] [ text pokemonInfo.name ]-- HTTPfetchPokemonNames : Cmd MsgfetchPokemonNames =  Http.get    { url = "https://pokeapi.co/api/v2/pokemon?limit=5"    , expect = Http.expectJson FetchedPokemonNames decoder    }pokemonInfoDecoder : Decoder PokemonInfopokemonInfoDecoder =  Json.Decode.map PokemonInfo    (Json.Decode.field "name" Json.Decode.string)decoder : Decoder (List PokemonInfo)    decoder =  Json.Decode.field "results" (Json.Decode.list pokemonInfoDecoder)

Publiquei esta pgina no editor online Ellie para que possa ver este webapp em funcionamento. Recomendo que tente alterar o cdigo e veja o que acontece. uma tima forma de comear a experimentar a linguagem Elm.

Analisando a implementao em Elm

No irei neste artigo explicar todo este cdigo e a arquitetura por trs da linguagem Elm. Mas queria destacar algumas partes importantes para o contexto da discusso deste artigo, comeando pela definio dos nossos types.

Definio de tipos

type alias PokemonInfo = { name : String }type Model  = Loading  | Failure  | Success (List PokemonInfo)

No cdigo acima primeiro definido um type alias, tornando mais claro para pessoa que est lendo o cdigo o que um PokemonInfo (neste caso, uma estrutura com um campo chamado name do tipo String). Isso tambm facilitar a vida do nosso compilador, permitindo que faa o tratamento de erro adequado quando necessrio e, durante a fase de compilao, consiga emitir mensagens de erros mais informativas.

Em seguida, definimos um type chamado Model que ser utilizado para representar o estado atual da nossa aplicao. Neste exemplo, nossa webapp pode estar em um (e apenas um) dos 3 possveis estados:

  • Loading: estado inicial da aplicao, indicando que a requisio http ainda est sendo processada.
  • Failure: representa um estado de falha, indicando que ocorreu algum problema ao realizar a chamada http ao servidor (podendo ser timeout, falha no parsing da mensagem de retorno, etc).
  • Success: indica que a requisio foi realizada e seu retorno convertido com sucesso.

Dos trs estados definidos, apenas o Success possui uma informao extra associada a ele: uma lista contendo elementos do tipo PokemonInfo. Note que isso no deixa espao para ambiguidades. Se tivermos um estado de sucesso, obrigatoriamente temos uma lista de PokemonInfo definida e com uma estrutura vlida. E o contrrio tambm: em caso de falha, a lista com os nomes dos Pokmons no estar definida.

A construo da pgina

Elm foi uma das pioneiras em utilizar o conceito de DOM virtual e programao declarativa no desenvolvimento de webapp.

Na arquitetura do Elm, existe uma separao bastante clara entre o estado da nossa aplicao e o que deve ser exibido na tela. responsabilidade da funo view montar, a partir do estado atual da nossa aplicao, uma representao da nossa DOM virtual. E toda vez que o estado for alterado (quando, por exemplo, terminar de carregar os dados com nomes dos Pokmons) esta funo ser reavaliada e uma nova DOM virtual criada.

Em nosso exemplo, isso ocorre no seguinte trecho de cdigo:

view : Model -> Html Msgview model =  case model of    Failure ->        text "Por alguma razo, no foi possvel carregar a lista com nome dos Pokmons. "    Loading ->      text "Carregando lista de nomes dos Pokmons, aguarde..."    Success pokemonsInfo ->      ul []        (List.map viewPokemonInfo pokemonsInfo) viewPokemonInfo : PokemonInfo -> Html MsgviewPokemonInfo pokemonInfo =  li [] [ text pokemonInfo.name ]

Temos aqui a declarao de 2 funes: a view e uma funo auxiliar chamada viewPokemonInfo.

Uma vantagem de utilizar types para representao do estado da nossa aplicao que sempre que um trecho de cdigo for utilizar este type, o compilador ir obrigar a pessoa desenvolvedora a tratar todos os possveis estados. Neste caso: Loading, Failure e Success. Se voc remover o tratamento do Loading da funo view do nosso exemplo, receber uma mensagem de erro similar a esta ao tentar compilar a aplicao:

Line 70, Column 3This `case` does not have branches for all possibilities:70|>  case model of71|>    Failure ->72|>        text "Por alguma razo, no foi possvel carregar a lista com nome dos Pokmons. "73|>74|>    Success pokemonsInfo ->75|>      ul []76|>        (List.map viewPokemonInfo pokemonsInfo) Missing possibilities include:    LoadingI would have to crash if I saw one of those. Add branches for them!Hint: If you want to write the code for each branch later, use `Debug.todo` as aplaceholder. Read <https://elm-lang.org/0.19.1/missing-patterns> for moreguidance on this workflow.

Isso traz mais segurana para a pessoa desenvolvedora refatorar o cdigo e incluir ou remover estados da aplicao, tendo a certeza que no vai deixar de tratar algum caso obscuro.

Fazendo uma chamada http

O trecho de cdigo abaixo responsvel por fazer a chamada http de forma assncrona e realizar o parse do retorno, transformando-o em uma lista de PokemonInfo.

fetchPokemonNames : Cmd MsgfetchPokemonNames =  Http.get    { url = "https://pokeapi.co/api/v2/pokemon?limit=5"    , expect = Http.expectJson FetchedPokemonNames decoder    }pokemonInfoDecoder : Decoder PokemonInfopokemonInfoDecoder =  Json.Decode.map PokemonInfo    (Json.Decode.field "name" Json.Decode.string)decoder : Decoder (List PokemonInfo)    decoder =  Json.Decode.field "results" (Json.Decode.list pokemonInfoDecoder)

Impossvel negar que este cdigo maior do que uma chamada a uma funo fetch. Mas note que este cdigo, alm de fazer a chamada de forma assncrona, tambm valida e transforma o retorno em uma List PokemonInfo, eliminando a necessidade de qualquer validao por nossa parte.

No final da execuo da chamada ser emitida uma mensagem FetchedPokemonNames junto com o resultado da operao: ou uma lista com nomes dos Pokmons j decodificados ou ento um resultado representando que ocorreu um erro.

Ser responsabilidade da funo update receber esta mensagem e criar um novo estado para a aplicao.

update : Msg -> Model -> (Model, Cmd Msg)update msg model =  case msg of    FetchedPokemonNames result ->      case result of        Ok pokemonsInfo ->          (Success pokemonsInfo, Cmd.none)        Err _ ->          (Failure, Cmd.none)

Mais uma vez, somos obrigados a tratar todos os possveis cenrios. Neste exemplo, so dois:

  • caso o result seja do tipo Ok, significa que nossa requisio foi processada com sucesso. retornado ento um novo estado para nossa aplicao, alterando para Success, junto com a lista contendo os nomes dos Pokmons.
  • caso o result seja do tipo Err, ento sabemos que ocorreu algum problema durante a requisio ou ao realizar o parsing do json. Um novo estado da aplicao retornado, alterando-o para Failure.

Sempre que o retorno da funo update for diferente do estado anterior, automaticamente a funo view ser acionada novamente, ento uma nova DOM virtual ser criada e eventuais alteraes sero aplicadas na tela. Para entender melhor este processo, voc pode ler sobre a The Elm Architecture nesta pgina.

Concluses

Embora tenha focado exclusivamente nas requisies http e no JavaScript, os mesmos conceitos so aplicados em muitos outros cenrios, bibliotecas, frameworks e linguagens.

Minha inteno no desmotivar o uso de JavaScript. Elm uma linguagem maravilhosa, mas at hoje ainda uso JavaScript e TypeScript em alguns webapps e este no o ponto focal do problema. O que eu gostaria que quando voc for consumir uma funo de sua linguagem preferida (seja uma funo nativa, seja de uma bibliotecas de terceiros), que voc sempre reflita e responda para si mesma: existe algum cenrio que este cdigo est ignorando? Ou, em outras palavras, esta uma soluo simples ou simplista?

E o mais importante: ao escrever uma nova funo, utilize uma interface de comunicao que incentive a pessoa que for consumi-la a seguir as boas prticas. Mesmo que ela esteja seguindo o caminho do mnimo esforo, deve ser capaz de se precaver de todos os cenrios possveis. Ou, em outras palavras, sempre siga o Princpio de menor espanto.

Gostou deste texto? Conhea meus outros artigos, podcasts e vdeos acessando: https://segunda.tech.


Original Link: https://dev.to/marciofrayze/codigo-simples-e-diferente-de-codigo-simplista-elm-vs-javascript-4cnf

Share this article:    Share on Facebook
View Full Article

Dev To

An online community for sharing and discovering great ideas, having debates, and making friends

More About this Source Visit Dev To