Resolução de Problema em Haskel

Resolução de Problema em Haskel
Photo by krakenimages / Unsplash

Olá skillers, eu de novo, vamos resolver um problema do último post, então fique comigo, vou descrever primeiro os requisitos.

Olá, entusiastas de Haskell! Hoje, vamos mergulhar em um desafio interessante e educativo: criar e testar funções em Haskell usando Hspec. Este post é perfeito para quem deseja aprimorar suas habilidades em Haskell e aprender mais sobre testes automatizados.

Requisitos do Desafio:

Objetivo: Escrever e testar funções em Haskell, utilizando a biblioteca Hspec para testes.

Funções a serem Implementadas:

  1. Incrementar: Cria uma função que recebe um número inteiro e retorna esse número incrementado em uma unidade.
  2. Dobrar: Desenvolve uma função que dobra o valor de um número inteiro fornecido.
  3. Elevar ao Quadrado: Implementa uma função que recebe um número inteiro e retorna o seu quadrado.
  4. Manipular Número: Elabora uma função que trata números de maneira diferente, dependendo se são pares ou ímpares. Se for par, subtrai 2; se for ímpar, multiplica por 3 e adiciona 1.

Testes com Hspec:

  • Configura e escreve testes para cada uma das funções acima, garantindo que elas funcionem conforme esperado.

Integração com app/Main.hs:

  • Demonstra como integrar e executar os testes no app/Main.hs, garantindo que não haja erros de compilação.

Para criar um passo a passo com testes em Haskell usando Hspec, vamos dividir o processo em várias etapas. Primeiro, escreveremos as funções que queremos testar, depois configuraremos os testes com Hspec, e finalmente, explicarei como testar no app/Main.hs


Crie um novo projeto e acesse a pasta

$ stack new calculator
$ cd calculator

Passo 1: Configurando Hspec

Crie um arquivo chamado test/FuncaoSpec.hs com o comando abaixo:

$ touch test/FuncaoSpec.hs

Agora, vamos criar os testes usando Hspec. Para isso, você precisa ter o Hspec instalado. Se não tiver, você pode adicioná-lo ao seu arquivo calculator/package.yaml

Adicicone o hspec em dependecies:

name:                calculator
version:             0.1.0.0
github:              "githubuser/calculator"
license:             BSD-3-Clause
author:              "Author name here"
maintainer:          "[email protected]"
copyright:           "2023 Author name here"

extra-source-files:
- README.md
- CHANGELOG.md

# Metadata used when publishing your package
# synopsis:            Short description of your package
# category:            Web

# To avoid duplicated efforts in documentation and dealing with the
# complications of embedding Haddock markup inside cabal files, it is
# common to point users to the README.md file.
description:         Please see the README on GitHub at <https://github.com/githubuser/calculator#readme>

dependencies:
- base >= 4.7 && < 5

ghc-options:
- -Wall
- -Wcompat
- -Widentities
- -Wincomplete-record-updates
- -Wincomplete-uni-patterns
- -Wmissing-export-lists
- -Wmissing-home-modules
- -Wpartial-fields
- -Wredundant-constraints

library:
  source-dirs: src

executables:
  calculator-exe:
    main:                Main.hs
    source-dirs:         app
    ghc-options:
    - -threaded
    - -rtsopts
    - -with-rtsopts=-N
    dependencies:
    - calculator

tests:
  calculator-test:
    main:                FuncaoSpec.hs
    source-dirs:         test
    ghc-options:
    - -threaded
    - -rtsopts
    - -with-rtsopts=-N
    dependencies:
    - calculator
    - hspec

Altere o main dos testes para a FuncaoSpec.hs

tests:
  calculator-test:
    main:                FuncaoSpec.hs
    source-dirs:         test

Vamos escrever os testes, eu recomendo escrever um it primeiro no arquivo test/FuncaoSpec.hs e depois execute o comando stack test e em seguida src/Funcao.hs , assim, você saberá o error que ocorre, quando não existir implementação.

import Test.Hspec
import Funcao

main :: IO ()
main = hspec $ do
  describe "Funcao" $ do
    it "incrementa um número" $ do
      incrementar 1 `shouldBe` 2

    it "dobra um número" $ do
      dobrar 2 `shouldBe` 4

    it "eleva um número ao quadrado" $ do
      elevarAoQuadrado 3 `shouldBe` 9

    it "manipula números ímpares" $ do
      manipularNumero [1, 3, 5] `shouldBe` [2, 4, 6]

    it "manipula números pares" $ do
      manipularNumero [2, 4, 6] `shouldBe` [4, 8, 12]

Passo 2: Escrevendo as Funções

Vamos começar escrevendo as funções incrementar, dobrar, e elevarAoQuadrado, além de uma função manipularNumero que trata números pares e ímpares de maneira diferente.

$ touch src/Funcao.hs
$ code .
module Funcao (
  incrementar,
  dobrar,
  elevarAoQuadrado,
  manipularNumero
) where

incrementar :: Int -> Int
incrementar x = x + 1

dobrar :: Int -> Int
dobrar x = x * 2

elevarAoQuadrado :: Int -> Int
elevarAoQuadrado x = x * x

manipularNumero :: [Int] -> [Int]
manipularNumero = map (\x -> if even x then dobrar x else incrementar x)

Você poderá adicionar step a step, primeiro crie o teste incrementar, depois execute os testes e analize o error, crie o incrementar e execute novamente os testes.

Este código Haskell define um módulo chamado Funcao que exporta quatro funções: incrementar, dobrar, elevarAoQuadrado e manipularNumero. Vamos analisar cada uma delas:

  1. incrementar :: Int -> Int
    • Esta função recebe um inteiro (Int) como argumento e retorna um inteiro.
    • incrementar x = x + 1: A função simplesmente adiciona 1 ao valor de entrada x. Por exemplo, incrementar 5 resultaria em 6.
  2. dobrar :: Int -> Int
    • Similarmente, esta função recebe um inteiro e retorna um inteiro.
    • dobrar x = x * 2: A função dobra o valor de entrada. Por exemplo, dobrar 3 resultaria em 6.
  3. elevarAoQuadrado :: Int -> Int
    • Esta função também recebe um inteiro e retorna um inteiro.
    • elevarAoQuadrado x = x * x: Ela eleva o número ao quadrado. Por exemplo, elevarAoQuadrado 4 resultaria em 16 (pois 4 * 4 = 16).
  4. manipularNumero :: [Int] -> [Int]
    • Esta função é um pouco mais complexa. Ela recebe uma lista de inteiros ([Int]) e retorna uma lista de inteiros.
    • manipularNumero = map (\x -> if even x then dobrar x else incrementar x): A função usa map para aplicar uma função anônima a cada elemento da lista. A função anônima verifica se um número x é par (usando even x). Se for par, ela dobra o número (usando a função dobrar definida anteriormente); se for ímpar, ela incrementa o número (usando a função incrementar). Por exemplo, manipularNumero [1, 2, 3, 4] resultaria em [2, 4, 4, 8].

Passo 3: Testando no app/Main.hs

Para testar no app/Main.hs, você precisa configurar seu projeto para incluir os testes. Aqui está um exemplo de como você pode fazer isso:

module Main where

import Funcao (incrementar, dobrar, elevarAoQuadrado, manipularNumero)

main :: IO ()
main = do
  print $ incrementar 5
  print $ dobrar 3
  print $ elevarAoQuadrado 4
  print $ manipularNumero [1, 2, 3, 4]

Execute os testes agora, stack test e assim teremos o resultado abaixo:

 calculator stack test

calculator> build (lib + exe + test)
Preprocessing library for calculator-0.1.0.0..
Building library for calculator-0.1.0.0..
Preprocessing executable 'calculator-exe' for calculator-0.1.0.0..
Building executable 'calculator-exe' for calculator-0.1.0.0..
Preprocessing test suite 'calculator-test' for calculator-0.1.0.0..
Building test suite 'calculator-test' for calculator-0.1.0.0..
calculator> copy/register
Installing library in /Users/ropeixoto/Project/youtube/haskell/calculator/.stack-work/install/x86_64-osx/8306faa983f5abaefc1c0174cee0b0c2f3d9546bcf08dd2d53dbf61d40958b21/9.4.7/lib/x86_64-osx-ghc-9.4.7/calculator-0.1.0.0-78A3o7dvJy0GQg12OMZoPX
Installing executable calculator-exe in /Users/ropeixoto/Project/youtube/haskell/calculator/.stack-work/install/x86_64-osx/8306faa983f5abaefc1c0174cee0b0c2f3d9546bcf08dd2d53dbf61d40958b21/9.4.7/bin
Registering library for calculator-0.1.0.0..
calculator> test (suite: calculator-test)


Funcao
  incrementa um número [✔]
  dobra um número [✔]
  eleva um número ao quadrado [✔]
  manipula números ímpares [✔]
  manipula números pares [✔]

Finished in 0.0009 seconds
5 examples, 0 failures



calculator> Test suite calculator-test passed
Completed 2 action(s).

Aqui temos um resultado de sucesso.

Já aprendemos mais sobre função e variável! Como declarar uma função e vários para o próximo post falar sobre lambdas.

Valeu skillers, até o próximo post.

Recursos Adicionais:

Read more