Desenvolvendo Funções de Divisão com TDD em Haskell
Neste post, vamos aplicar TDD para desenvolver funções de divisão em Haskell, abordando desde divisões simples até o tratamento de divisão por zero.
Passo 1: Configurar o Ambiente
Certifique-se de ter o Stack, uma ferramenta de desenvolvimento Haskell, instalada.
Passo 2: Criar um Novo Projeto
No terminal, crie um novo projeto Stack:
stack new divisionProject
cd divisionProject
Passo 3: Adicionar Dependências de Teste
No arquivo package.yaml
, adicione hspec
às dependências de teste:
tests:
divisionProject-test:
main: Spec.hs
source-dirs: test
dependencies:
- divisionProject
- hspec
Passo 4: Escrever Testes
Crie um arquivo de teste em test/Spec.hs
com os seguintes cenários:
import Test.Hspec
import Lib
main :: IO ()
main = hspec $ do
describe "divisionFunction" $ do
it "correctly divides two numbers" $ do
divisionFunction 10 2 `shouldBe` Just 5
it "handles division by a larger number" $ do
divisionFunction 5 10 `shouldBe` Just 0.5
it "returns Nothing when dividing by zero" $ do
divisionFunction 5 0 `shouldBe` Nothing
Execute o teste com o comando:
stack test
Resultado
[1 of 2] Compiling Main
/Users/ropeixoto/Project/youtube/haskell/divisionProject/test/Spec.hs:8:7: error:
Variable not in scope: divisionFunction :: t0 -> t1 -> Maybe a0
|
8 | divisionFunction 10 2 `shouldBe` Just 5
| ^^^^^^^^^^^^^^^^
[2 of 2] Compiling Paths_divisionProject
Progress 1/2
Error: [S-7282]
Stack failed to execute the build plan.
While executing the build plan, Stack encountered the error:
[S-7011]
While building package divisionProject-0.1.0.0 (scroll up to its
section to see the error) using:
/Users/ropeixoto/.stack/setup-exe-cache/x86_64-osx/Cabal-simple_6HauvNHV_3.8.1.0_ghc-9.4.7 --verbose=1 --builddir=.stack-work/dist/x86_64-osx/Cabal-3.8.1.0 build lib:divisionProject exe:divisionProject-exe test:divisionProject-test --ghc-options " -fdiagnostics-color=always"
Process exited with code: ExitFailure 1
➜ divisionProject stack test
divisionProject> build (lib + exe + test)
Preprocessing library for divisionProject-0.1.0.0..
Building library for divisionProject-0.1.0.0..
Preprocessing executable 'divisionProject-exe' for divisionProject-0.1.0.0..
Building executable 'divisionProject-exe' for divisionProject-0.1.0.0..
Preprocessing test suite 'divisionProject-test' for divisionProject-0.1.0.0..
Building test suite 'divisionProject-test' for divisionProject-0.1.0.0..
[1 of 2] Compiling Main
/Users/ropeixoto/Project/youtube/haskell/divisionProject/test/Spec.hs:8:7: error:
Variable not in scope: divisionFunction :: t4 -> t5 -> Maybe a2
|
8 | divisionFunction 10 2 `shouldBe` Just 5
| ^^^^^^^^^^^^^^^^
/Users/ropeixoto/Project/youtube/haskell/divisionProject/test/Spec.hs:11:7: error:
Variable not in scope: divisionFunction :: t2 -> t3 -> Maybe a1
|
11 | divisionFunction 5 10 `shouldBe` Just 0.5
| ^^^^^^^^^^^^^^^^
/Users/ropeixoto/Project/youtube/haskell/divisionProject/test/Spec.hs:14:7: error:
Variable not in scope: divisionFunction :: t0 -> t1 -> Maybe a0
|
14 | divisionFunction 5 0 `shouldBe` Nothing
| ^^^^^^^^^^^^^^^^
Progress 1/2
Error: [S-7282]
Stack failed to execute the build plan.
While executing the build plan, Stack encountered the error:
[S-7011]
While building package divisionProject-0.1.0.0 (scroll up to its
section to see the error) using:
/Users/ropeixoto/.stack/setup-exe-cache/x86_64-osx/Cabal-simple_6HauvNHV_3.8.1.0_ghc-9.4.7 --verbose=1 --builddir=.stack-work/dist/x86_64-osx/Cabal-3.8.1.0 build lib:divisionProject exe:divisionProject-exe test:divisionProject-test --ghc-options " -fdiagnostics-color=always"
Process exited with code: ExitFailure 1
Passo 5: Implementar a Função
No arquivo src/Lib.hs
, implemente a função divisionFunction
:
module Lib
( divisionFunction
) where
divisionFunction :: Float -> Float -> Maybe Float
divisionFunction _ 0 = Nothing
divisionFunction x y = Just (x / y)
Agora altere o nome no arquivo app/Main.hs
:
module Main (main) where
import Lib
main :: IO ()
main = print(divisionFunction 2 10)
Vamos decompor a sintaxe da função divisionFunction
em Haskell para torná-la mais compreensível. Haskell tem uma sintaxe única, especialmente se você está acostumado com linguagens imperativas, mas uma vez que você entende a lógica por trás dela, tudo se torna mais claro.
Assinatura da Função
divisionFunction :: Float -> Float -> Maybe Float
divisionFunction
é o nome da função.::
é usado para denotar a assinatura da função.Float -> Float -> Maybe Float
descreve os tipos dos argumentos e do retorno da função.Float -> Float
indica que a função aceita dois argumentos, ambos do tipoFloat
.-> Maybe Float
indica que a função retorna um valor do tipoMaybe Float
.
Corpo da Função
A função divisionFunction
é definida usando correspondência de padrões (pattern matching), uma característica poderosa em Haskell.
divisionFunction _ 0 = Nothing
divisionFunction x y = Just (x / y)
divisionFunction _ 0 = Nothing
: Esta linha lida com o caso de divisão por zero._
é um caractere curinga que significa "qualquer valor". Neste contexto, indica que o primeiro argumento pode ser qualquerFloat
.0
especifica que o segundo argumento deve ser zero.= Nothing
é o valor retornado pela função quando o segundo argumento é zero.Nothing
é um valor do tipoMaybe Float
, que é usado para representar a ausência de um valor válido (neste caso, porque não podemos dividir por zero).
divisionFunction x y = Just (x / y)
: Esta linha lida com todos os outros casos.x
ey
são variáveis que representam os dois argumentosFloat
.= Just (x / y)
calcula a divisão dex
pory
e retorna o resultado embrulhado emJust
.Just
é um construtor para o tipoMaybe
, que neste caso, encapsula um valorFloat
válido.
Passo 6: Executar o Teste
Execute o teste com o comando:
stack test
Se os testes passarem, sua função de divisão está correta e lida com os casos de divisão por zero.
➜ divisionProject stack test
divisionProject> build (lib + exe + test)
Preprocessing library for divisionProject-0.1.0.0..
Building library for divisionProject-0.1.0.0..
Preprocessing executable 'divisionProject-exe' for divisionProject-0.1.0.0..
Building executable 'divisionProject-exe' for divisionProject-0.1.0.0..
[1 of 2] Compiling Main [Source file changed]
[3 of 3] Linking .stack-work/dist/x86_64-osx/Cabal-3.8.1.0/build/divisionProject-exe/divisionProject-exe [Objects changed]
Preprocessing test suite 'divisionProject-test' for divisionProject-0.1.0.0..
Building test suite 'divisionProject-test' for divisionProject-0.1.0.0..
[1 of 2] Compiling Main
[3 of 3] Linking .stack-work/dist/x86_64-osx/Cabal-3.8.1.0/build/divisionProject-test/divisionProject-test
divisionProject> copy/register
Installing library in /Users/ropeixoto/Project/youtube/haskell/divisionProject/.stack-work/install/x86_64-osx/8306faa983f5abaefc1c0174cee0b0c2f3d9546bcf08dd2d53dbf61d40958b21/9.4.7/lib/x86_64-osx-ghc-9.4.7/divisionProject-0.1.0.0-BerzMQAkBpW5ecYpuZ6lkN
Installing executable divisionProject-exe in /Users/ropeixoto/Project/youtube/haskell/divisionProject/.stack-work/install/x86_64-osx/8306faa983f5abaefc1c0174cee0b0c2f3d9546bcf08dd2d53dbf61d40958b21/9.4.7/bin
Registering library for divisionProject-0.1.0.0..
divisionProject> test (suite: divisionProject-test)
divisionFunction
correctly divides two numbers [✔]
handles division by a larger number [✔]
returns Nothing when dividing by zero [✔]
Finished in 0.0008 seconds
3 examples, 0 failures
divisionProject> Test suite divisionProject-test passed
Completed 2 action(s).
Agora já entendemos como funciona o tratamento de erros com o Haskel, vamos continuar avançando nos conteúdo. Há muito do que conhecermos e entendermos.
Encorajo vocês a mexerem, removerem os testes de divisão com zero e tentar dividir por zero no Main.hs e futucar no código, quebrar, ler os erros, isso ajuda vocês a compreenderem.
Valeu skillers,
Nos vemos em breve nos próximos posts. Até lá, happy coding! 🚀📚