Closures e Aplicação Parcial em Haskell
Haskell, sendo uma linguagem de programação funcional, oferece recursos poderosos como closures e aplicação parcial. Vamos explorar esses conceitos com exemplos práticos.
1. Capturando Valores em uma Expressão Lambda
Em Haskell, uma expressão lambda é uma função anônima que pode capturar valores do seu escopo. Isso é útil para criar funções que mantêm um estado ou contexto.
Exemplo:
criarSaudacao :: String -> String
criarSaudacao nome = (\saudacao -> saudacao ++ ", " ++ nome)
Neste exemplo, a função criarSaudacao
retorna uma expressão lambda que captura o valor de nome
.
2. Usando Closures para Criar Novas Funções
Um closure é uma função que captura e mantém o estado de variáveis externas. No exemplo anterior, a expressão lambda captura nome
, formando um closure.
Uso do Closure:
let saudacaoParaJoao = criarSaudacao "João"
print (saudacaoParaJoao "Olá") -- Saída: "Olá, João"
A função saudacaoParaJoao
é um closure que mantém o valor "João".
3. Simplificação com Aplicação Parcial
A aplicação parcial é uma técnica onde uma função é chamada com menos argumentos do que ela requer, retornando uma nova função que espera os argumentos restantes.
Exemplo de Aplicação Parcial:
somar :: Int -> Int -> Int
somar x y = x + y
somarCinco :: Int -> Int
somarCinco = somar 5
Aqui, somarCinco
é uma aplicação parcial da função somar
, onde o primeiro argumento é fixado em 5.
Uso:
print (somarCinco 10) -- Saída: 15
A aplicação parcial é útil para criar funções mais específicas a partir de funções mais genéricas.
Closures e aplicação parcial são conceitos fundamentais em Haskell que permitem criar funções poderosas e flexíveis. Closures capturam e mantêm estados, enquanto a aplicação parcial permite a reutilização de funções com diferentes conjuntos de argumentos. Essas características são essenciais para a programação funcional, oferecendo um alto nível de abstração e reutilização de código.
Exemplo Prático
Vamos criar um exemplo prático em Haskell demonstrando o uso de closures e aplicação parcial, seguindo a metodologia de Desenvolvimento Orientado por Testes (BDD - Behavior-Driven Development). O projeto consistirá em uma função que gera saudações personalizadas e outra que soma um número fixo a outro número. Vamos detalhar cada passo, desde a criação do projeto até a execução dos testes.
Passo 1: Criar o Projeto
- Instalar o Stack: Certifique-se de ter o Haskell Stack instalado.
- Criar um Novo Projeto:
stack new closureExample cd closureExample
Passo 2: Configurar o package.yaml
- Abra o arquivo
package.yaml
. - Adicione a dependência
hspec
para testes:dependencies: - base >= 4.7 && < 5 tests: closureExample-test: main: Spec.hs source-dirs: test dependencies: - closureExample - hspec
Passo 3: Escrever Testes
-
Criar o Arquivo de Teste:
- Crie um arquivo
Spec.hs
na pastatest
.
- Crie um arquivo
-
Escrever Testes:
Spec.hs
deve parecer com isso:import Test.Hspec import Lib main :: IO () main = hspec $ do describe "Closure e Aplicação Parcial" $ do it "gera uma saudação personalizada" $ do let saudacaoParaJoao = criarSaudacao "João" saudacaoParaJoao "Olá" `shouldBe` "Olá, João" it "soma cinco a um número" $ do somarCinco 10 `shouldBe` 15
Passo 4: Implementar o Código
- Modificar
Lib.hs
:- No diretório
src
, abraLib.hs
. - Adicione as funções que serão testadas:
module Lib ( criarSaudacao , somarCinco ) where criarSaudacao :: String -> String -> String criarSaudacao nome = \saudacao -> saudacao ++ ", " ++ nome somar :: Int -> Int -> Int somar x y = x + y somarCinco :: Int -> Int somarCinco = somar 5
- No diretório
O código em questão é um exemplo de uma função em Haskell que demonstra o conceito de closures. Vamos analisá-lo detalhadamente:
criarSaudacao :: String -> String -> String
criarSaudacao nome = \saudacao -> saudacao ++ ", " ++ nome
-
Declaração da Função:
criarSaudacao :: String -> String -> String
é a assinatura da função. Ela indica quecriarSaudacao
é uma função que recebe umaString
(onome
) e retorna uma função do tipoString -> String
. Esta função retornada espera umaString
(asaudacao
) e também retorna umaString
.
-
Definição da Função:
criarSaudacao nome = \saudacao -> saudacao ++ ", " ++ nome
é a definição da função. Aqui,criarSaudacao
recebe um argumentonome
, e retorna uma função lambda.
-
Função Lambda:
\saudacao -> saudacao ++ ", " ++ nome
é uma função lambda. Esta função anônima recebe um argumentosaudacao
e concatena (usando++
) estasaudacao
com uma vírgula e onome
fornecido à funçãocriarSaudacao
.
-
Closure:
- O conceito de closure é demonstrado aqui. A função lambda
\saudacao -> saudacao ++ ", " ++ nome
"captura" o valor denome
que foi fornecido quandocriarSaudacao
foi chamada. Mesmo após a execução decriarSaudacao
, a função lambda retida ainda tem acesso aonome
que foi passado para ela.
- O conceito de closure é demonstrado aqui. A função lambda
Exemplo de Uso:
Se você chamar criarSaudacao "João"
, isso retornará uma nova função que espera uma saudação. Por exemplo:
let saudacaoParaJoao = criarSaudacao "João"
Agora, saudacaoParaJoao
é uma função que você pode usar para criar uma saudação personalizada para "João". Por exemplo:
saudacaoParaJoao "Olá" -- Isso resultará em "Olá, João"
Neste exemplo, a função saudacaoParaJoao
é uma closure que mantém o valor "João" e o utiliza sempre que é chamada com uma nova saudação.
Passo 5: Modificar a Main.hs
- Alterar
Main.hs
:- No diretório
app
, editeMain.hs
para usar as funções:module Main where import Lib main :: IO () main = do let saudacaoParaJoao = criarSaudacao "João" print $ saudacaoParaJoao "Olá" print $ somarCinco 10
- No diretório
Passo 6: Executar os Testes
- Executar Testes:
- Volte para o diretório raiz do projeto e execute:
stack test
- Volte para o diretório raiz do projeto e execute:
➜ closureExample stack test
closureExample-0.1.0.0: unregistering (local file changes: test/Spec.hs)
closureExample> build (lib + exe + test)
Preprocessing library for closureExample-0.1.0.0..
Building library for closureExample-0.1.0.0..
Preprocessing executable 'closureExample-exe' for closureExample-0.1.0.0..
Building executable 'closureExample-exe' for closureExample-0.1.0.0..
Preprocessing test suite 'closureExample-test' for closureExample-0.1.0.0..
Building test suite 'closureExample-test' for closureExample-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/closureExample-test/closureExample-test [Objects changed]
closureExample> copy/register
Installing library in /Users/ropeixoto/Project/youtube/haskell/closureExample/.stack-work/install/x86_64-osx/8306faa983f5abaefc1c0174cee0b0c2f3d9546bcf08dd2d53dbf61d40958b21/9.4.7/lib/x86_64-osx-ghc-9.4.7/closureExample-0.1.0.0-OyDDZm2yClIgqO83KmHyv
Installing executable closureExample-exe in /Users/ropeixoto/Project/youtube/haskell/closureExample/.stack-work/install/x86_64-osx/8306faa983f5abaefc1c0174cee0b0c2f3d9546bcf08dd2d53dbf61d40958b21/9.4.7/bin
Registering library for closureExample-0.1.0.0..
closureExample> test (suite: closureExample-test)
Closure e Apliocação Parcial
gera uma saudação personalizada [✔]
soma cinco a um número [✔]
Finished in 0.0008 seconds
2 examples, 0 failures
closureExample> Test suite closureExample-test passed
Completed 2 action(s).
Passo 7: Executar o Projeto
- Executar o Projeto:
- Para ver o projeto em ação, execute:
stack run
- Para ver o projeto em ação, execute:
Explicação do Código
criarSaudacao
: Esta função demonstra um closure. Ela retorna uma função lambda que captura onome
e espera umsaudacao
para completar a mensagem.somarCinco
: Esta é uma aplicação parcial da funçãosomar
. Fixamos o primeiro argumento em 5 e retornamos uma nova função que espera o segundo argumento.
É isso skillers, até mais! <3