Primeiros passos com Elm
Elm é uma linguagem funcional, com tipos estáticos, perfeita para desenvolver web apps de forma segura.
##Instalação
Você pode instalar Elm facilmente através do npm
ou yarn
:
$ npm i -g elm
Porém eu prefiro instalar o Elm via Homebrew:
$ brew install elm
Para garantir que o executável foi instalado corretamente, basta executar
elm --version
no terminal, por exemplo:
$ elm --version
0.19.1
##Primeiro app
No terminal, crie um novo diretório, por exemplo first-elm-app
e entre
no diretório:
$ mkdir first-elm-app && cd first-elm-app
Em seguida, inicialize o projeto com Elm neste diretório:
$ elm init
Hello! Elm projects always start with an elm.json file. I can create them!
Now you may be wondering, what will be in this file? How do I add Elm files to
my project? How do I see it in the browser? How will my code grow? Do I need
more directories? What about tests? Etc.
Check out <https://elm-lang.org/0.19.1/init> for all the answers!
Knowing all that, would you like me to create an elm.json file now? [Y/n]:
Aqui talvez seja o seu primeiro contato com o compilador do Elm. É normal ele avisar tudo que vai ser feito antes de ser feito, nos mínimos detalhes. Estes detalhes inclusive são aplicados durante o desenvolvimento e a etapa de compilação.
Você pode não curtir a sintaxe de Elm, mas aposto que vai se apaixonar pelo compilador!
Seguindo, responda a pergunta do compilador teclando Enter
.
Se você listar o conteúdo do diretório, verá que o Elm criou um arquivo chamado
elm.json
. Esse arquivo faz mais ou menos o mesmo trabalho do package.json
em
projetos Node.js.
$ ls
elm.json src
Agora é um bom momento para inicializar um repositorio Git!
##Configurando a suíte de testes
Um dos pontos mais importantes em um projeto são os testes! A facilidade da escrita
e execução dos testes é essencial. Vamos começar instalando o elm-test
através do NPM.
$ npm i -g elm-test
elm-test
funciona mais ou menos como o jest e depende
de alguns outros pacotes. Vamos configurar o nosso app para aceitar testes:
$ elm-test init
Here is my plan:
Add:
elm/random 1.0.0
elm-explorations/test 1.2.2
Would you like me to update your elm.json accordingly? [Y/n]:
Novamente o Elm avisa o que vai fazer e pede a confirmação!
Ao aceitar, o programa elm-test
vai criar o arquivo de teste de exemplo
tests/Example.elm
, além de adicionar algumas dependências no arquivo elm.json
.
module Example exposing (..)
import Expect exposing (Expectation)
import Fuzz exposing (Fuzzer, int, list, string)
import Test exposing (..)
suite : Test
suite =
todo "Implement our first test. See https://package.elm-lang.org/packages/elm-explorations/test/latest for how to do this!"
Você pode notar que foi criada uma pasta chamada elm-stuff
com algumas coisas dentro.
Essa pasta é equivalente ao node_modules
.
$ tree
.
├── elm-stuff
│ └── generated-code
│ └── elm-community
│ └── elm-test
│ └── 0.19.1-revision7
│ └── install
│ ├── elm-stuff
│ │ └── 0.19.1
│ │ ├── d.dat
│ │ ├── i.dat
│ │ └── o.dat
│ └── elm.json
├── elm.json
├── src
└── tests
└── Example.elm
10 directories, 6 files
Para evitar de adicionar a pasta elm-stuff
no
histórico do Git, vou ignorar ela criando um arquivo .gitignore
:
$ echo /elm-stuff > .gitignore
O arquivo de exemplo com os testes não possui muitas coisas, mas pode ser amendrontador para quem nunca viu algum programa escrito em uma sintaxe parecida com Haskell ou a família das linguagens ML.
Elm é muito inspirada em Haskell, e mesmo que possa parecer assustador em primeiro momento, não é um bicho de sete cabeças.
Para executar os testes, basta executar elm-test
no terminal:
$ elm-test
Compiling > Starting tests
elm-test 0.19.1-revision7
-------------------------
Running 1 test. To reproduce these results, run: elm-test --fuzz 100 --seed 99034205465976
TEST RUN INCOMPLETE because there is 1 TODO remaining
Duration: 132 ms
Passed: 0
Failed: 0
Todo: 1
↓ Example
◦ TODO: Implement our first test. See https://package.elm-lang.org/packages/elm-explorations/test/latest for how to do this!
Mesmo sem entender a sintaxe do Elm, pelo output do elm-test
já dá pra entender
que o programa conseguiu encontrar o nosso arquivo de teste, tentou executar os testes
escritos naquele arquivo e encontrou um teste incompleto (que na verdade é o único escrito).
Vamos testar alguma coisa útil, se 1+1 é igual a 2:
module Example exposing (..)
import Expect exposing (Expectation)
import Fuzz exposing (Fuzzer, int, list, string)
import Test exposing (..)
suite : Test
suite =
test "Should 1+1 be equal to 1"
(\_ -> Expect.equal (1+1) 2)
Ao executar os testes novamente, tudo verde.
$ elm-test
Compiling > Starting tests
elm-test 0.19.1-revision7
-------------------------
Running 1 test. To reproduce these results, run: elm-test --fuzz 100 --seed 4535202811949
TEST RUN PASSED
Duration: 98 ms
Passed: 1
Failed: 0
Mas provavelmente você está se perguntando o que diabos é isso aqui (\_ -> Expect.equal (1+1) 2)
.
Elm tem suporte a funções anônimas assim como em JavaScript. Traduzindo esse trecho de código em JavaScript, seria algo tipo:
;() => Expect.equal(1 + 1, 1)
Em Elm a sintaxe é um pouco diferente. Aqui vai mais um exemplo de função anonima:
\x -> x + 1
\x y -> x + y
Em JavaScript seria:
(x) => x + 1
(x, y) => x + y
A função test
, importada do módulo Test
aceita um título para o teste e
uma função que executa o teste. No caso do arquivo de exemplo, é mais prático
passar uma função anônima.
Usando JavaScript e Jest, o mesmo teste poderia ter a seguinte cara:
test('Should 1+1 be equal to 1', () => Expect.equal(1 + 1, 2))
Vou alterar o arquivo de teste e incluir a função describe
:
module Example exposing (..)
import Expect exposing (Expectation)
import Fuzz exposing (Fuzzer, int, list, string)
import Test exposing (..)
suite : Test
suite =
describe "Example tests"
[ test "Should 1+1 be equal to 1"
(\_ -> Expect.equal (1 + 1) 2)
]
A função describe
aceita um título e uma lista com testes. Vamos adicionar um
novo teste e checar se a concatenação "hello" ++ "world"
é igual a "hello world"
:
module Example exposing (..)
import Expect exposing (Expectation)
import Fuzz exposing (Fuzzer, int, list, string)
import Test exposing (..)
suite : Test
suite =
describe "Example tests"
[ test "Should 1+1 be equal to 1"
(\_ -> Expect.equal (1 + 1) 2)
, test "Should concat 'hello' and 'world'"
(\_ -> Expect.equal ("hello " ++ "world") "hello world")
]
Ao executar elm-test
vemos que os dois testes foram executados com sucesso!
$ elm-test
Compiling > Starting tests
elm-test 0.19.1-revision7
-------------------------
Running 2 tests. To reproduce these results, run: elm-test --fuzz 100 --seed 72391561032834
TEST RUN PASSED
Duration: 184 ms
Passed: 2
Failed: 0
##Feedback automático
Ao trabalhar com TDD, é muito prático colocar o test runner para executar
automaticamente sempre que um arquivo do código fonte for alterado. É possível
fazer isso com o elm-test
usando o parâmetro --watch
:
$ elm-test --watch
Compiling > Starting tests
elm-test 0.19.1-revision7
-------------------------
Running 2 tests. To reproduce these results, run: elm-test --fuzz 100 --seed 36570578976867
TEST RUN PASSED
Duration: 159 ms
Passed: 2
Failed: 0
Watching for changes...
Vou alterar o arquivo tests/Example.elm
e introduzir um erro:
[ test "Should 1+1 be equal to 1"
(\_ -> Expect.equal (1 + 1) 1)
O elm-test
automaticamente compilou o código fonte e executou os testes, mostrando
o erro:
↓ Example
↓ Example tests
✗ Should 1+1 be equal to 1
1
╷
│ Expect.equal
╵
2
TEST RUN FAILED
Duration: 219 ms
Passed: 1
Failed: 1
Watching for changes...
Caso eu use alguma função de asserção que não exista, como por exemplo:
[ test "Should 1+1 be equal to 1"
(\_ -> Expect.dummy (1 + 1) 2)
O compilador me avisa de uma forma muito amigável:
I cannot find a `Expect.dummy` variable:
12| (\_ -> Expect.dummy (1 + 1) 2)
^^^^^^^^^^^^
The `Expect` module does not expose a `dummy` variable. These names seem close
though:
Expect.all
Expect.atMost
Expect.equal
Expect.err
##Recursos
Se você gostou desse tutorial, talvez possa se interessar pelas fontes que usei.