Começando com Elm – programação funcional no frontend

Olá amigos, tudo bem? Vamos falar de Elm neste post. Ele será uma ótima introdução para quem está começando com Elm.

English version of this post / versão em inglês deste post

Bem, o que é Elm? O Elm (http://elm-lang.org/) é uma linguagem de programação funcional que compila para Javascript, pensada para desenvolver interfaces para web apps. Neste sentido, ela pode ser considerada uma alternativa ao React, mas com uma série de características particulares.

Como ela compila para Javascript, você pode adicioná-la em seu projeto aos poucos, convertendo apenas uma pequena parte dele e adicionando o Javascript gerado pelo Elm.

Uma das grandes vantagens de Elm é que ela é estaticamente tipada. Isso que quer dizer que o compilador chega o código e garante que o seu código vai funcionar, sem a possibilidade de erros e exceções causadas por tipos inconsistentes ou variáveis `undefined`.

Em testes realizados, Elm também se mostra mais rápido que o React.

Se isso foi suficiente para chamar sua atenção, vamos agora começar a usar Elm.

Instalação

Para instalação, existem instaladores no site oficial para Windows e Mac. Outra opção é instalar através do npm. Em todo caso, clique aqui e vai lá conferir como fazer.

Básico do Elm

Após a instalação, você pode acessar seu terminal e digitar elm-repl. Isto vai abrir o console do Elm, onde você poderá executar comandos da linguagem.

Strings

Primeiro vamos às strings. Strings são definidas por aspas duplas. Não adianta usar aspas simples, pois você receberá uma mensagem de erro, indicando que deve ser feita a troca para aspas duplas.

> "oi"
"oi" : String
> 'oi'
-- SYNTAX PROBLEM -------------------------------------------- repl-temp-000.elm

Elm uses double quotes for strings. Switch the ' to " on both ends of the string
and you should be all set!

3|   'oi'
        ^
Maybe <http://elm-lang.org/docs/syntax> can help you figure it out.

Para concatenar, usamos ++:

> "oi" ++ ", " ++ "tudo bem?"
"oi, tudo bem?" : String

Números e operações matemáticas

Operações matemáticas básicas também são o que você espera… soma, subtração, multiplicação, divisão e potenciação usam sinais comumente utilizados em outras linguagens.

> 3 + 6
9 : number
> 7 - 4
3 : number
> 8 * 5
40 : number
> 10 / 2
5 : Float

Repare que a divisão do Elm é por padrão, uma divisão com decimais, retornando sempre um float. Assim como ocorre com Python 3, para divisão com inteiros é possível usar //. Para o resto da divisão, usamos rem, indicando os dois números a serem divididos:

> 7 // 2
3 : Int
> rem 8 3
2 : Int

Funções

Para escrever uma função, declaramos seu nome e o parâmetro a ser recebido, e então, após o sinal de =, escrevemos a operação que queremos fazer. Por exemplo, uma função isNegative, que retorna se um número é negativo, ficaria assim:

> isNegative n = n < 0
<function> : number -> Bool
> isNegative 99
False : Bool
> isNegative -345
True : Bool
> isNegative (8 - 9)
True : Bool

Repare que para chamar a função, passamos parâmetros logo após seu nome, apenas com espaços. Caso a função pedisse mais um parâmetro, a forma de utilizá-la seria isNegative 3 4. Fica bem limpo.

Condicional – If

Caso você queira trabalhar com condições, você pode usar um bloco if:

> if True then "valor verdadeiro" else "valor falso"
"valor verdadeiro" : String
> if False then "valor verdadeiro" else "valor falso"
"valor falso" : String

Ao contrário de outras linguagens, Elm não associa a outros tipos um booleano, como o Python, por exemplo, onde uma lista vazia é considerado um valor False e pode ser utilizado em comparações. Caso você tente utilizar algo que não seja um boolean em uma comparação, você receberá uma mensagem de erro:

> if "" then "valor verdadeiro" else "valor falso"
-- TYPE MISMATCH --------------------------------------------- repl-temp-000.elm

This condition does not evaluate to a boolean value, True or False.

3|   if "" then "valor verdadeiro" else "valor falso"
        ^^
You have given me a condition with this type:

    String

But I need it to be:

    Bool

Hint: Elm does not have "truthiness" such that ints and strings and lists are
automatically converted to booleans. Do that conversion explicitly.

Repare que a mensagem de erro é bem detalhada, e ajuda muito a resolver o problema encontrado nesse caso.

Por fim, você pode utilizar condições junto com funções. Veja este exemplo:

> over9000 powerLever = \
|   if powerLever > 9000 then "maior que 9000" else "seu verme fracote"
<function> : number -> String
> over9000 50
"seu verme fracote" : String
> over9000 9001
"maior que 9000" : String
> over9000 150000
"maior que 9000" : String

Ao usar a barra invertida no elm-repl, ele entende que você quer continuar seu código na próxima linha. Para que o código funcione, você deve adicionar espaços na segunda linha. Neste caso adicionei 2. Em Elm, a identação faz parte da sintaxe e do código.

Listas

Listas são como os arrays do Javascript, ou as próprias listas do Python, estruturas de dados usadas para armazenar múltiplos valores / dados. Em Elm, os valores de uma lista tem que ser todos do mesmo tipo. Para definir uma lista, seus itens são incluídos dentro de colchetes, separados por vírgulas:

> ["Felipe", "Gustavo", "Gabriel"]
["Felipe","Gustavo","Gabriel"] : List String
> [1, 2, 4, 9]
[1,2,4,9] : List number
> [1, "2", 3]
-- TYPE MISMATCH --------------------------------------------- repl-temp-000.elm

The 1st and 2nd entries in this list are different types of values.

3|   [1, "2", 3]
         ^^^
The 1st entry has this type:

    number

But the 2nd is:

    String

Hint: Every entry in a list needs to be the same type of value. This way you
never run into unexpected values partway through. To mix different types in a
single list, create a "union type" as described in:
<http://guide.elm-lang.org/types/union_types.html>

Você também pode aplicar alguns métodos em listas. Vamos ver alguns interessantes:

> names = ["Felipe", "Gustavo", "Gabriel"]
["Felipe","Gustavo","Gabriel"] : List String
> empty_list = []
[] : List a
> List.isEmpty names
False : Bool
> List.isEmpty empty_list
True : Bool
> List.length names
3 : Int
> List.reverse names
["Gabriel","Gustavo","Felipe"] : List String
> numbers = [1, 30, 10, 9, 29]
[1,30,10,9,29] : List number
> List.sort numbers
[1,9,10,29,30] : List number
> List.member "Felipe" names
True : Bool
> List.member "Jose" names
False : Bool

E por fim, você pode usar map para aplicar uma função pré-definida em cada item de uma lista:

> double n = n * 2
<function> : number -> number
> List.map double numbers
[2,60,20,18,58] : List number

Para uma lista completas de métodos usados em listas, confira este link no site oficial do Elm.

Tuplas

Tuplas são uma estrutura de dados que podem armazenar um número fixo de valores / dados, sendo que cada valor em uma tupla pode ser de um tipo diferente, ao contrário das listas. Um possível uso para as tuplas é quando você precisa retornar mais de um valor de uma função. Por exemplo:

> validPassword password = \
|   if String.length password <= 6 then \
|     (False, "Password tem que ter mais de 6 caracteres") \
|   else \
|     (True, "Password validada")
<function> : String -> ( Bool, String )
> validPassword "abc"
(False,"Password tem que ter mais de 6 caracteres") : ( Bool, String )
> validPassword "abcd1234"
(True,"Password validada") : ( Bool, String )

As tuplas, entretanto, são um pouco limitadas. Para casos de uso mais complexos, você provavelmente deve usar um Record, que nada mais são que os dicionários do Python, objetos do Javascript.

Record / registro

Um record é um conjunto de pares chave-valor. Para criar um, você deve, dentro de chaves {}, definir os nomes das chaves e os valores das mesmas, através do sinal de igual e com os pares separados por vírgulas:

> felipe = { sobrenome = "Galvao", idade = 31, profissao = "desenvolvedor" }
{ sobrenome = "Galvao", idade = 31, profissao = "desenvolvedor" }
    : { idade : number, profissao : String, sobrenome : String }
> felipe.sobrenome
"Galvao" : String
> felipe.idade
31 : number

Como pudemos ver, uma forma de acessar um valor em um record é através de record.chave. Existe outra forma, que se assemelha a uma função, onde você declara .chave record.

> .profissao felipe
"desenvolvedor" : String
> .peso felipe
-- TYPE MISMATCH --------------------------------------------- repl-temp-000.elm

The argument to this function is causing a mismatch.

3|   .peso felipe
           ^^^^^^
This function is expecting the argument to be:

    { b | peso : ... }

But it is:

    { idade : ..., profissao : ..., sobrenome : ... }

Hint: The record fields do not match up. One has peso. The other has idade,
profissao, and sobrenome.

Repare como, ao pedir uma chave que não existe no record, obviamente recebemos uma mensagem de erro.

Por fim, você também pode criar funções que operem com valores de um record. Basta definir a função normalmente, mas com a chave que irá conter o valor a ser usado na função dentro das chaves:

> menorDe40 { idade } = idade < 40
<function> : { a | idade : number } -> Bool
> menorDe40 felipe
True : Bool
> abimael = { sobrenome = "Souza", idade = 67 }
{ sobrenome = "Souza", idade = 67 } : { idade : number, sobrenome : String }
> menorDe40 abimael
False : Bool

Para atualizar valores de um record, usamos a barra vertical |. O update do valor de um record no Elm é não destrutivo, o que quer dizer que ele retorna um novo objeto, com o valor da chave atualizado e os valores das outras chaves inalterados. Para atualizar múltiplos valores, basta adicionar uma vírgula e o novo valor para a chave desejada:

> { felipe | sobrenome = "Skywalker" }
{ sobrenome = "Skywalker", idade = 31, profissao = "desenvolvedor" }
    : { idade : number, profissao : String, sobrenome : String }
> { felipe | sobrenome = "Skywalker", profissao = "Jedi" }
{ sobrenome = "Skywalker", idade = 31, profissao = "Jedi" }
    : { idade : number, profissao : String, sobrenome : String }

Se quiser manter as atualizações, basta salvar o novo record em uma nova variável. O record original será mantido inalterado:

> luke = { felipe | sobrenome = "Skywalker", profissao = "Jedi" }
{ sobrenome = "Skywalker", idade = 31, profissao = "Jedi" }
    : { idade : number, profissao : String, sobrenome : String }
> luke
{ sobrenome = "Skywalker", idade = 31, profissao = "Jedi" }
    : { idade : number, profissao : String, sobrenome : String }
> felipe
{ sobrenome = "Galvao", idade = 31, profissao = "desenvolvedor" }
    : { idade : number, profissao : String, sobrenome : String }

Conclusão

Acredito que com isso cobrimos a maior parte do básico do Elm. No próximo post vamos falar sobre como começar a usar o Elm no mundo real, construindo interfaces para aplicações web.

One comment on “Começando com Elm – programação funcional no frontend

Deixe uma resposta

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *