Da Confusão à Clareza: Minha Jornada de Aprendizado em Haskell e o Poder dos Paradigmas Funcionais

Da Confusão à Clareza: Minha Jornada de Aprendizado em Haskell e o Poder dos Paradigmas Funcionais
Photo by Shahadat Rahman / Unsplash

Lembro-me vividamente do início da minha jornada com Ruby. Era um mundo novo para mim, repleto de conceitos como lambda e map, que pareciam desafiadores à primeira vista. Mas, à medida que me aprofundava, comecei a apreciar a eficiência que essas ferramentas ofereciam.

Então, veio a transição para React. Aqui, me deparei com as Arrow Functions, um conceito que, inicialmente, me deixou perplexo. Confesso que meus primeiros dias com React foram marcados por uma aversão quase instantânea. Parecia um labirinto sem fim de novos paradigmas e práticas.

Durante meus anos na universidade, tive a oportunidade de explorar uma variedade de linguagens de programação, incluindo Haskell. Naquela época, eu estava convencido de que a academia estava desatualizada, ensinando teorias que eu jamais usaria na prática. Que engano meu! Eu, então um programador júnior, não conseguia ver o valor desses conhecimentos fundamentais.

Com o tempo e a experiência acumulada, percebi a importância de revisitar os conceitos básicos. Trabalhando com React na SmartFit, enfrentei desafios significativos. Foi essa dificuldade que me impulsionou a entender verdadeiramente a base do React e sua filosofia subjacente. Esse entendimento renovado despertou em mim um interesse em estudar Haskell.

A lição mais valiosa que aprendi é que o estudo não se limita ao que aplicamos imediatamente. Estudar Haskell, por exemplo, não significa que irei trabalhar diretamente com essa linguagem. O verdadeiro valor está na compreensão profunda que esses conhecimentos fundamentais oferecem.

Portanto, antes de mergulharmos em React, Elixir ou qualquer outra linguagem funcional, quero encorajar vocês a fortalecerem suas bases. Vamos juntos mudar a percepção de que programação é um campo inacessível e difícil de aprender. Com paciência e dedicação, podemos desvendar os mistérios da programação e torná-la uma jornada empolgante e gratificante.

Vamos começar.

Haskell

Haskell (linguagem de programação) – Wikipédia, a enciclopédia livre

Haskell é uma linguagem de programação que surgiu no final dos anos 80 e início dos anos 90, como um esforço para consolidar ideias de várias linguagens funcionais existentes na época. Vamos explorar um pouco mais sobre sua origem, ideias fundamentais, sintaxe básica e usos.

Haskell Curry - https://culturacientifica.com/2019/10/30/si-no-me-equivoco-6465/

Origem

  • Desenvolvimento: Haskell foi desenvolvida por um comitê de pesquisadores em linguagens de programação, iniciado em 1987. O nome "Haskell" é uma homenagem ao lógico Haskell Curry, cujo trabalho em lógica matemática serve de base para muitos conceitos em linguagens funcionais.
  • Lançamento: A primeira versão padrão, Haskell 1.0, foi lançada em 1990.

Ideias Fundamentais

  1. Programação Funcional Pura: Haskell é uma linguagem puramente funcional, o que significa que as funções não têm efeitos colaterais. Isso facilita o raciocínio sobre o código e a verificação formal.
  2. Avaliação Preguiçosa (Lazy Evaluation): Haskell adia a computação até que o resultado seja realmente necessário. Isso permite, por exemplo, a definição de estruturas de dados infinitas.
  3. Tipagem Forte e Estática: Haskell possui um sistema de tipos muito expressivo que ajuda a evitar erros em tempo de compilação.
  4. Inferência de Tipo: O compilador é capaz de deduzir automaticamente os tipos em muitos casos, reduzindo a necessidade de anotações de tipo explícitas.
  5. Monads: Haskell usa monads para lidar com efeitos colaterais, como entrada/saída, de uma maneira que se integra com a natureza puramente funcional da linguagem.

Vamos para a prática e entender um pouco da sintaxe, não será um post aprofundado sobre Haskell, mas vamos entender os conceitos básicos e também a aplicabilidade.

Instalação Haskell

Instalar Haskell em diferentes sistemas operacionais, como macOS, Windows e Linux, pode ser feito de maneira relativamente simples. A forma mais comum de instalar Haskell é através do Haskell Platform ou do Stack, que são gerenciadores de pacotes e ambientes de desenvolvimento para Haskell. Vamos ver como você pode fazer isso em cada sistema operacional:

macOS

Usando o Homebrew:

  1. Instale o Homebrew (se ainda não estiver instalado) executando no Terminal:
    /bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"
    
  2. Instale o Haskell Stack com o Homebrew:
    brew install haskell-stack
    
  3. Verifique a instalação executando:
    stack --version
    

Usando o Haskell Platform:

  1. Baixe o Haskell Platform para macOS do site oficial.
  2. Siga as instruções de instalação no instalador.

Windows

Usando o Chocolatey:

  1. Instale o Chocolatey (se ainda não estiver instalado) seguindo as instruções em chocolatey.org.
  2. Abra o PowerShell como administrador e execute:
    choco install haskell-dev
    refreshenv
    
  3. Verifique a instalação com:
    ghc --version
    

Usando o Haskell Platform:

  1. Baixe o Haskell Platform para Windows do site oficial.
  2. Execute o instalador e siga as instruções.

Linux

Usando o gerenciador de pacotes (Ubuntu/Debian):

  1. Abra o terminal e atualize a lista de pacotes:
    sudo apt update
    
  2. Instale o Stack:
    sudo apt install haskell-stack
    
  3. Verifique a instalação:
    stack --version
    

Usando o Haskell Platform:

  1. Baixe o Haskell Platform para Linux do site oficial.
  2. Siga as instruções específicas para a sua distribuição Linux.

Configuração Pós-Instalação

Após instalar o Haskell, você pode querer configurar o ambiente de desenvolvimento:

  1. Configure o Stack (opcional para usuários do Stack):
    stack setup
    
  2. Crie seu primeiro projeto Haskell para testar a instalação:
    stack new myproject
    cd myproject
    stack build
    stack exec myproject-exe
    code myproject
    

Lembre-se de que as versões e os métodos de instalação podem variar, então é sempre uma boa ideia verificar a documentação oficial mais recente do Haskell para obter informações atualizadas.

Abrindo o seu Visual Studio Code, verá os arquivos abaixo:


O comando stack new myproject cria um novo projeto Haskell usando o Stack, que é um gerenciador de ambiente e de dependências para Haskell. Vamos detalhar o que cada um desses comandos faz e o que você pode esperar encontrar dentro da pasta do projeto myproject:

1. stack new myproject

  • O que faz: Este comando cria um novo projeto Haskell chamado myproject em um diretório também chamado myproject. O Stack utiliza um template padrão para gerar a estrutura do projeto, que inclui arquivos de código-fonte, um arquivo de configuração do projeto, entre outros.
  • Arquivos criados: Dentro da pasta myproject, você encontrará vários arquivos e diretórios. Os mais importantes são:
    • stack.yaml: Um arquivo de configuração para o Stack, especificando a versão do compilador GHC a ser usada e outras configurações do projeto.
    • myproject.cabal: Um arquivo de descrição do pacote que contém metadados sobre o projeto, como dependências, módulos, versão, etc.
    • app/: Um diretório contendo o código-fonte do projeto. Por padrão, há um arquivo Main.hs que é o ponto de entrada do programa.
    • src/: Este diretório também contém código-fonte, que poderá ser usada em todo o projeto.
    • test/: Um diretório para testes unitários do projeto.

2. cd myproject

  • O que faz: Este comando muda o diretório atual para a pasta myproject que foi criada pelo comando anterior.

3. stack build

  • O que faz: Este comando compila o projeto. O Stack verifica as dependências listadas no arquivo .cabal, baixa o que for necessário, compila as dependências e, em seguida, compila o seu projeto.
  • Processo: Durante a compilação, o Stack pode baixar a versão correta do compilador GHC (se ainda não estiver instalada) e todas as bibliotecas necessárias.

4. stack exec myproject-exe

  • O que faz: Este comando executa o programa compilado. myproject-exe é o nome padrão do executável gerado pelo Stack, baseado no nome do projeto e na configuração do arquivo .cabal.

Criando o nosso Hello, Haskell!

Vamos alterar o projeto para criar um simples chamado "Hello Haskell" usando TDD. Este projeto consistirá em uma função que retorna a string "Hello, Haskell!" e um teste para essa função.

Passo 1: Alter o Projeto

Abra um terminal e crie um acesse a página que criamos do projeto Stack:

cd myproject

Isso entrará na pasta chamada myproject com uma estrutura de projeto básica que já vimos.

Passo 2: Adicionar Dependências de Teste

Edite o arquivo package.yaml e adicione as dependências de teste. Você pode usar o Hspec, que é uma biblioteca de testes popular para Haskell. Adicione hspec na seção de dependências:

tests:
  myproject-test:
    main:                Spec.hs
    source-dirs:         test
    dependencies:
      - myproject
      - hspec

Passo 3: Escrever um Teste

Antes de escrever a função, vamos escrever um teste para ela. Crie um arquivo de teste em test/Spec.hs:

import Test.Hspec
import Lib

main :: IO ()
main = hspec $ do
  describe "sayHello" $ do
    it "returns the string 'Hello, Haskell!'" $ do
      sayHello `shouldBe` "Hello, Haskell!"

Este teste verifica se a função sayHello retorna a string "Hello, Haskell!".

import Test.Hspec
import Lib

main :: IO ()
main = hspec $ do
  describe "sayHello" $ do
    it "returns the string 'Hello, Haskell!'" $ do
      sayHello `shouldBe` "Hello, Haskell!"

  1. Importações (import):
    • import Test.Hspec: Importa a biblioteca Hspec, que é usada para escrever testes.
    • import Lib: Importa o módulo Lib (geralmente definido em src/Lib.hs), que contém a definição da função sayHello.
  2. Definição de Função (main :: IO ()):
    • main :: IO (): Esta é a assinatura da função main. Ela indica que main é uma função que realiza ações de entrada/saída (IO) e retorna uma unidade (()), que é um tipo que representa "nenhum valor útil" ou "vazio".
    • main = hspec $ do: Aqui, a função main é definida. Ela usa hspec para executar um conjunto de testes. O símbolo $ é um operador que permite evitar parênteses extras. do é usado para encadear várias ações IO.
  3. Blocos de Teste (describe, it):
    • describe "sayHello": Define um grupo de testes para a função sayHello. describe é uma função que agrupa vários testes relacionados.
    • it "returns the string 'Hello, Haskell!'": Define um teste específico. it é usado para descrever um único caso de teste.
    • Dentro do bloco it, o código sayHello shouldBe "Hello, Haskell!" é o teste em si. shouldBe é um operador de asserção que verifica se o valor à esquerda (sayHello) é igual ao valor à direita ("Hello, Haskell!").
  4. Função sayHello:
    • Embora não mostrada aqui, sayHello é uma função definida em algum lugar no módulo Lib que, quando chamada, deve retornar a string "Hello, Haskell!".

Volte ao terminal e execute o teste:

stack test
➜  myproject stack test
myproject> build (lib + exe + test)
Preprocessing library for myproject-0.1.0.0..
Building library for myproject-0.1.0.0..
Preprocessing test suite 'myproject-test' for myproject-0.1.0.0..
Building test suite 'myproject-test' for myproject-0.1.0.0..
[1 of 2] Compiling Main

/Users/ropeixoto/Project/youtube/haskell/myproject/test/Spec.hs:8:7: error: Variable not in scope: sayHello :: String
  |
8 |       sayHello `shouldBe` "Hello, Haskell!"
  |       ^^^^^^^^
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 myproject-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:myproject exe:myproject-exe test:myproject-test --ghc-options " -fdiagnostics-color=always"
       Process exited with code: ExitFailure 1


Não temos a função sayHello, por isso o error já nos direciona, se não sabe inglês, copia os erros no translator, chatgpt ou em qualquer lugar, mas por favor, leia o error.

Passo 4: Implementar a Função

Agora, implemente a função sayHello no arquivo src/Lib.hs , vamos alterar o nome da função e alterar de IO para String, pedindo para que retorne a string.

module Lib
    ( sayHello
    ) where

import Lib

sayHello :: String
sayHello =  putStrLn "Hello, Haskell!"

depois, vamos no nosso app/Main.hs e alteremos o someFunc para o nosso sayHello

Passo 5: Executar o Teste

Volte ao terminal e execute o teste:

stack test

Se tudo estiver correto, o teste passará, indicando que a função sayHello se comporta conforme esperado.

➜  myproject stack test
myproject> build (lib + exe + test)
Preprocessing library for myproject-0.1.0.0..
Building library for myproject-0.1.0.0..
Preprocessing test suite 'myproject-test' for myproject-0.1.0.0..
Building test suite 'myproject-test' for myproject-0.1.0.0..
Preprocessing executable 'myproject-exe' for myproject-0.1.0.0..
Building executable 'myproject-exe' for myproject-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/myproject-exe/myproject-exe [Objects changed]
myproject> copy/register
Installing library in /Users/ropeixoto/Project/youtube/haskell/myproject/.stack-work/install/x86_64-osx/8306faa983f5abaefc1c0174cee0b0c2f3d9546bcf08dd2d53dbf61d40958b21/9.4.7/lib/x86_64-osx-ghc-9.4.7/myproject-0.1.0.0-ANIgRu2cNkuIDCsHjtzTQQ
Installing executable myproject-exe in /Users/ropeixoto/Project/youtube/haskell/myproject/.stack-work/install/x86_64-osx/8306faa983f5abaefc1c0174cee0b0c2f3d9546bcf08dd2d53dbf61d40958b21/9.4.7/bin
Registering library for myproject-0.1.0.0..
myproject> test (suite: myproject-test)


sayHello
  returns the string 'Hello, Haskell!' [✔]

Finished in 0.0005 seconds
1 example, 0 failures



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


Ótimo ter passado esse momento com vocês, então bom código e sucesso na carreira! :)

Nosso teste passou, então vamos para os próximos posts.

Read more