Buscar

Apostila de Haskell

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 3, do total de 288 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 6, do total de 288 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes
Você viu 9, do total de 288 páginas

Faça como milhares de estudantes: teste grátis o Passei Direto

Esse e outros conteúdos desbloqueados

16 milhões de materiais de várias disciplinas

Impressão de materiais

Agora você pode testar o

Passei Direto grátis

Você também pode ser Premium ajudando estudantes

Prévia do material em texto

07/04/2016 Introdução ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/introduction 1/3
Índice Começando
Introdução
Sobre este tutorial
Bem­vindo ao Aprender Haskell será um grande bem para você! Se você já esta lendo isto, existem boas chances de você
querer aprender Haskell. Muito bem, você já esta no lugar certo, mas antes vamos conversar um pouco sobre este tutorial.
Eu decidi escrever porque estava querendo solidificar meus conhecimentos em Haskell e porque pensei que poderia ajudar
as pessoas novas em Haskell a aprende­lo sob a minha perspectiva. Este é só mais um tutorial sobre Haskell pairando pela
internet. Quando eu comecei em Haskell eu não aprendi a partir de apenas uma fonte. O meu caminho para o aprendizado
passou pela leitura de diversos e diferentes tutoriais e artigos porque cada um explicava algo seguindo por um caminho
diferente do outro. Ao passar por diferentes fontes, eu consegui juntar as peças e tudo acabou caindo no mesmo lugar.
Portanto, esta é apenas mais uma fonte adicional a se utilizar para aprender Haskell para que você tenha uma maior chance
de encontrar uma que você realmente goste.
Este tutorial é destinado a pessoas que tenham experiência em linguagens de
programação imperativa (C, C++, Java, Python …) sem terem programado antes em
uma linguagem funcional (Haskell, ML, OCaml …). Apesar de que eu aposto que se
você não tiver uma larga experiência em programação, um rápido capítulo como o
que você irá acompanhar vai habilitá­lo a aprender Haskell.
O canal #haskell (ou #haskell­br) na rede freenode é um belo local para mandar
alguma questão caso você estiver emperrado. As pessoas lá são extremamente
legais, pacienciosas e entendem os newbies.
Eu falhei aproximadamente 2 vezes antes de finalmente aprender Haskell, isto porque tudo me parecia muito estranho e eu
não conseguia entender. Mas logo em seguida, após ultrapassar essa primeira barreira, me veio o "estalo" e então entendi
que era tudo muito fácil. Acho que o que estou tentando dizer é o seguinte: Haskell é muito legal e se você realmente tiver
interesse em programação você deve continuar mesmo que ele lhe pareça estranho. Aprender Haskell é muito parecido com
quando se está aprendendo a programar pela primeira vez — isto que é divertido! Ele te força a pensar diferente, o que nos
remete ao próximo capítulo …
Então, o que é Haskell?
Haskell é uma linguagem de programação puramente funcional. Em linguagens de
programação imperativas você pensa nas coisas seguindo uma sequência computacional de
tarefas sendo executadas, embora durante o processo possam mudar de estado. Por exemplo,
você define uma variável como 5 e então faz alguma coisa e em seguida a define como sendo
alguma outra coisa qualquer. Você possui o controle do fluxo da estrutura podendo fazer uma
determinada ação diversas vezes. Na programação puramente funcional você não diz para o
computador o que fazer, porém muitas vezes você diz em qual coisa está. O fatorial de um número
07/04/2016 Introdução ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/introduction 2/3
é o produto de todos os números sobre 1 deste número, a soma de uma lista de números é o primeiro mais a soma de todos
os outros números e assim por diante. Você expressa isto na forma de funções. Você também não pode definir uma variável
como sendo alguma coisa e em seguida defini­lá como sendo alguma outra coisa mais tarde. Se você disser a uma função
que algo é 5, você não poderá dizer depois que é alguma outra coisa porque você já disse que era 5. O que você é? Algum
tipo de mentiroso? Então, em linguagens puramente funcionais, uma função não tem efeitos colaterais. A única coisa que
podemos fazer com uma função é calcular algo e devolvê­lo como um resultado. Inicialmente, eu vi isto como uma limitação,
porém isto realmente tem algumas consequências interessantes: se a função é chamada duas vezes com os mesmos
parâmetros, isto garantirá o retorno de um mesmo resultado. Isso se chama transparência referencial e não só permite que
o compilador raciocine sobre o comportamento do programa, como também permite que você deduza facilmente (e até
mesmo prove) que uma função está correta e, em seguida, construa funções mais complexas juntando diversas funções
simples por "colagem.
Haskell é preguiçoso. Isso significa que a menos que seja especificamente dito de
outra forma, Haskell não irá executar funções e calcular as coisas antes que ele seja
realmente obrigado a lhe mostrar um resultado. Isto vai bem de encontro com a
transparência referencial que lhe permite pensar em programas como uma série de
transformações nos dados. Isto também permite simplificar coisas como
estruturas de dados infinitas. Digamos que você tem uma lista de números imutáveis
xs = [1,2,3,4,5,6,7,8] e uma função doubleMe que multiplica cada elemento
por 2 e em seguida retorna uma nova lista. Caso quiséssemos multiplicar nossa lista
por 8 em uma linguagem imperativa fazendo
doubleMe (doubleMe (doubleMe (xs ))), ele provavelmente iria passar uma vez
pela lista, fazer uma cópia e retornar. Em seguida, ele iria passar por mais duas vezes e retornar o resultado. Em uma
linguagem preguiçosa, chamando doubleMe numa lista sem forçar para que seja mostrado algum resultado assim que
acabar, ela irá lhe dizer "Sim sim, faço isso mais tarde". Mas uma vez que você queira ver o resultado, o primeiro doubleMe
dirá para o segundo que quer o resultado do 1, agora! O segundo 1 irá dizer para o terceiro 1 e o terceiro 1 relutantemente
irá retornar o 1 duplicado, com um 2. O segundo 1 recebido irá retornar um 4 para o primeiro. O primeiro 1 olhará aquilo e
dirá para você que o primeiro elemento é 8. Por isso, ele só passa uma vez pela lista e só quando você realmente precisa
dela. Dessa maneira, quando você quiser alguma coisa em uma linguagem "preguiçosa", você poderá ter apenas alguns
dados iniciais e eficientemente transformá­los e melhorá­los para que eles se assemelhem com aquilo que você quer no final.
Haskell é estaticamente tipado. Quando você compilar seu programa, o compilador saberá
quais partes do código é um número, o que é uma string e assim por diante. Isso significa que
uma série de possíveis erros poderão ser capturados em tempo de compilação. Se você tentar
adicionar um número a uma string, o compilador irá se queixar de você. Haskell usa um bom
sistema de tipos que tem inferência de tipo. Isso significa que você não precisa explicitamente
identificar cada pedaço de código com um tipo porque o sistema de tipos inteligente descobrirá
muito sobre ele. Se você disser a = 5 + 4 , você não precisará dizer para o Haskell que a é um
número, ele irá descobrir isso por si só. Inferência de tipo também permite que o seu código seja mais genérico. Se você fizer
uma função de soma com dois parâmetros e não declarar explicitamente seus tipos, ela irá funcionar com quaisquer valores
que sejam números.
Haskell é elegante e conciso. Isto porque ele utiliza uma série de conceitos de alto nível, programas Haskell são
normalmente mais curtos do que os seus equivalentes imperativos. E programas mais curtos são mais fáceis de se manter e
têm menos bugs.
07/04/2016 Introdução ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/introduction 3/3
Haskell foi feito por caras realmente inteligentes (com doutorado). O trabalho sobre Haskell começou em 1987 quando uma
comissão de pesquisadores se reuniu para projetar uma linguagem matadora. Em 2003, o Relatório Haskell foi publicado já
com uma versão estável da linguagem.
O que eu preciso para embarcar nessa
Um editor de texto e um compilador Haskell. Provavelmente você já tem um editor de texto favorito instalado, então não perca
tempo com isso. Neste tutorial iremos usar o GHC, o compilador Haskell mais usado. O melhor modo de iniciar na linguagem
é baixando oHaskell Platform, que é algo como o Haskell com esteróides.
GHC pode pegar um script Haskell (que normalmente tem uma extensão .hs) e compilá­lo, mas ainda existe um modo
interativo que permite que você interativamente interaja com os scripts. Interativamente. Você pode chamar funções de
scripts carregados que os resultados serão exibidos imediatamente. Para o seu aprendizado é muito mais fácil e rápido que
compilar toda vez que fizer uma alteração e enfim executar o programa a partir do prompt. O modo interativo é chamado
digitando ghci no seu prompt. Se você definir algumas funções em um arquivo chamado, digamos, myfunctions.hs, você
poderá carregar essas funções digitando : l myfunctions para então poder brincar com elas, desde que myfunctions.hs
esteja na mesma pasta na qual o ghci foi invocado. Se você mudar o seu script ".hs, basta executar : l myfunctions
novamente ou fazer : r , que é o equivalente a recarregar o script atual. O processo comum para mim quando estou
brincando, é definir algumas funções em um arquivo .hs, carregá­lo e modificá­lo sem pudor. Quando preciso, carrego um
outro arquivo .hs novamente e assim por diante. Isto é também o que faremos aqui.
Índice Começando
07/04/2016 Começando ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/starting­out 1/16
Introdução Índice Tipos e Typeclasses
Começando
Preparar, apontar, foi!
Ok, vamos começar! Se você é o tipo de pessoa horrível que não lê as introduções das
coisas e se você pulou ela, então de qualquer maneira você irá querer ler a última seção
da introdução, porque explica o que você precisa para acompanhar este tutorial e como
vamos fazer para carregar as funções. A primeira coisa que vamos fazer é rodar o GHC
no modo interativo e chamar alguma função para obter uma base para começar a sentir
o Haskell. Abra seu terminal e digite ghci . Você será agraciado com algo parecido com
isto:
GHCi, version 6.8.2: http://www.haskell.org/ghc/  :? for help  
Loading package base ... linking ... done.  
Prelude>  
Meus parabéns, você esta no GHCi! O prompt aqui é Prelude> no entando como isto é demasiadamente longo quando você
carrega algumas coisas na sua sessão, nós iremos usar ghci>. Se você quer ter o mesmo prompt, digite
:set prompt "ghci> ".
Veja algumas simples operações aritméticas.
ghci> 2 + 15  
17  
ghci> 49 * 100  
4900  
ghci> 1892 ‐ 1472  
420  
ghci> 5 / 2  
2.5  
ghci>  
Isto é bastante auto­explicativo. Nós podemos utilizar todos operadores comuns em uma só linha e todas suas regras usuais
precedentes serão obedecidas. Nós também podemos utilizar parênteses para declarar de forma explicita a operação
precedente ou para muda­la.
ghci> (50 * 100) ‐ 4999  
1  
ghci> 50 * 100 ‐ 4999  
1  
ghci> 50 * (100 ‐ 4999)  
‐244950  
Muito legal hein?! Sim, eu sei que não... mas tenha paciência comigo. Uma pequena armadilha a se observar aqui é a
negação de números. Se você quiser obter números negativos, sempre tenha a operação determinada por parênteses.
07/04/2016 Começando ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/starting­out 2/16
Fazendo 5 * ‐3 o GHCi irá gritar com você, porém fazendo 5 * (‐3) irá funcionar perfeitamente.
Álgebra booleana também é bastante simples. Você provavelmente já sabe que && refere­se a expressão booleana and, ||
refere­se a expressão booleana or. not faz a negação de um True ou de um False.
ghci> True && False  
False  
ghci> True && True  
True  
ghci> False || True  
True   
ghci> not False  
True  
ghci> not (True && True)  
False  
Testando para igualdades fica algo assim.
ghci> 5 == 5  
True  
ghci> 1 == 0  
False  
ghci> 5 /= 5  
False  
ghci> 5 /= 4  
True  
ghci> "hello" == "hello"  
True   
Que tal se fizermos 5 + "llama" ou 5 == True? Bem, se nós tentarmos o primeiro trecho destes código, nós teremos uma
grande e assustadora mensagem de erro!
No instance for (Num [Char])  
arising from a use of `+' at <interactive>:1:0‐9  
Possible fix: add an instance declaration for (Num [Char])  
In the expression: 5 + "llama"  
In the definition of `it': it = 5 + "llama"   
Credo! O que o GHCi esta dizendo para nós aqui é que "llama" não é um número e que ele não sabe como adicionar isto ao
número 5. Se isso não fosse um "llama" mas um "four" ou um "4", Haskell ainda assim não iria considerá­lo como um
número. + espera­se que tenha a sua esquerda e a sua direita um número. Se nós tentarmos fazer True == 5, o GHCi irá
nos dizer que os tipos são diferentes. Visto que o + funciona somente em coisas consideradas números, o == já funciona em
quaisquer coisas que podem ser comparadas. O problema é que ambos devem ser do mesmo tipo. Não podemos comparar
maças com laranjas. Iremos dar uma olhada em tipos um pouco mais tarde.
Nota: Você pode fazer 5 + 4.0 porque 5 é adaptável e pode se comportar como um inteiro ou um ponto flutuante (float).
4.0 não pode se comportar como um inteiro, então o 5 é o único que pode ser adaptado.
Talvez você não saiba mas nós estamos usando função agora o tempo todo. Por exemplo, * é uma função que pega dois
números e então os multiplica. Como você percebe, nós invocamos ela colocando no meio dos dois números. Isto é o que
nós chamamos de função infixa. Muitas funções não são usadas com números pois são as funções de prefixo. Vamos dar
uma olhada então nisto.
07/04/2016 Começando ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/starting­out 3/16
Funções normalmente são prefixo, porém agora não iremos declarar explicitamente que a função
é do formato prefixo, iremos apenas assumir isto. Na maioria das linguagens imperativas as
funções são chamadas escrevendo o nome da função e então escrevendo os seus parâmetros
entre parênteses, normalmente separados por vírgula. Em Haskell, as funções são chamadas
escrevendo o nome da função, com um espaço e então os parâmetros separados por espaços.
Inicialmente, vamos tentar chamar uma das mais chatas funções em Haskell.
ghci> succ 8  
9   
A função succ pega qualquer coisa e então define o seu sucessor e o retorna. Como você pode ver, nós apenas separamos
o nome da função dos parâmetros com um espaço. Chamar a função com alguns parâmetros também é bastante simples. As
funções min e max pegam duas coisas que podem ser colocadas em ordem (como um número!) e retorna aquele que for o
menor ou maior. Veja você mesmo:
ghci> min 9 10  
9  
ghci> min 3.4 3.2  
3.2  
ghci> max 100 101  
101   
Aplicar uma função (chamando a função colocando um espaço depois dela, e depois ir digitando os seus parâmetros) tem
sempre a maior precedência. O que isto significa para nós é que estas duas declarações são equivalentes.
ghci> succ 9 + max 5 4 + 1  
16  
ghci> (succ 9) + (max 5 4) + 1  
16  
No entanto, se nós quisermos ter o sucessor do produto dos números 9 e 10, nós não podemos escrever succ 9 * 10
senão isto irá pegar o sucessor do 9, que seria então a multiplicação por 10. Resultando em 100. Nós devemos escrever
succ (9 * 10) para obter 91.
Se a função recebe dois parâmetros, nós podemos também chamá­la como uma função infixa colocando antes dela aspas
simples. Por exemplo, se a função div recebe dois inteiros e realiza a divisão deles. Fazendo div 92 10 o resultado será 9.
Porém quando nós realizamos este tipo de chamada ela fica meio confusa para saber qual número esta dividindo e qual esta
sendo dividido. Nós podemos invocar esta função como uma função infixa fazendo 92 `div` 10 e logicamente isto ficará
muito mais claro.
Muitas pessoas que vieram de linguagens imperativas tendem a colocar a notação dos parênteses para especificar o uso de
funções. Por exemplo, em C, você utiliza parênteses para chamar funções como foo(), bar(1) ou baz(3, "haha"). Como
dissemos, espaços são utilizados para a aplicação de funçõesem Haskell. Então estas funções em Haskell deveriam ser foo,
bar 1 e baz 3 "haha". Se você enxergar algo como bar (bar 3), não significa que aquela chamada de bar recebe bar e 3
como parâmetros. Isto significa que nós primeiros chamamos a função bar com o parâmetro 3 para obter algum número e
então chamar bar novamente com este número. Em C, isso seria algo como bar(bar(3)).
Primeiras funções do seu filho
07/04/2016 Começando ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/starting­out 4/16
No capítulo anterior nós aprendemos a idéia básica para chamada de funções. Agora vamos tentar fazer a nossa própria!
Abra o seu editor de texto favorito e escreva a seguinte função que recebe um número e o multiplica por dois.
doubleMe x = x + x  
Funções são definidas de forma semelhante como são chamadas. O nome da função é seguido por parâmetros separados
por espaços. Porém quando definimos funções, terá um = e depois nós iremos definir o que a função faz. Salve isto como
baby.hs ou algo parecido. Agora vá até o local onde o arquivo foi salvo e rode o ghci a partir dai. Dentro do GCHI, digite
:l baby. Agora o script estará carregado e nós já podemos brincar com a função que definimos.
ghci> :l baby  
[1 of 1] Compiling Main             ( baby.hs, interpreted )  
Ok, modules loaded: Main.  
ghci> doubleMe 9  
18  
ghci> doubleMe 8.3  
16.6   
Como o + funciona muito bem tanto com números inteiros como com números de ponto flutuante (ou qualquer outra coisa
que possa ser considerada um número) a nossa função também funcionára com qualquer número. Vamos fazer uma função
que recebe dois números e multiplica cada um deles por dois, e após realiza a soma deles.
doubleUs x y = x*2 + y*2   
Simples. Nós podemos definir isso também como doubleUs x y = x + x + y + y. Testar isto produzirá um resultado
bastante previsível (lembre­se de anexar esta função no arquivo baby.hs, salva­lo e então rodar :l baby dentro do GHCI).
ghci> doubleUs 4 9  
26  
ghci> doubleUs 2.3 34.2  
73.0  
ghci> doubleUs 28 88 + doubleMe 123  
478  
Como esperado, você pode chamar suas próprias funções dentro de outras funções que você fez. Com isto em mente, nós
iremos redefinir doubleUs da seguinte forma:
doubleUs x y = doubleMe x + doubleMe y   
Este é um exemplo bastante simples de um padrão comum que você verá ao longo de Haskell. Ir fazendo funções básicas
que são obviamente corretas e então combiná­las em funções mais complexas. Desta forma você também acabará evitando
repetições. E se alguns matemáticos descobrissem que 2 é na verdade 3 e você tivesse que mudar o seu programa? Você
deveria então simplesmente redefinir o doubleMe para ser x + x + x e desde que doubleUs chamasse doubleMe, já estaria
funcionando automaticamente neste estranho mundo onde 2 é 3.
Funções em Haskell não precisam estar em uma ordem em particular, portanto não importa se você definir primeiro o
doubleMe e depois o doubleUs ou se fizer o contrário.
07/04/2016 Começando ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/starting­out 5/16
Agora vamos fazer uma função que multiplicará um número por 2 porém somente se este número for menor ou igual a 100,
porque números maiores que 100 já são grandes o suficiente.
doubleSmallNumber x = if x > 100  
                        then x  
                        else x*2   
Neste ponto iremos introduzir o comando if do Haskell. Você provavelmente esta familiarizado com o
comando if de outras linguagens. A diferença entre o comando if do Haskell e o comando if das
linguagens imperativas é que a parte do else é obrigatória em Haskell. Em linguagens imperativas
você pode simplesmente pular uma séria de etapas se a condição do if não for satisfatória, porém
em Haskell toda expressão e função devem retornar alguma coisa. Poderíamos também escrever o
comando if em uma só linha, mas eu acho esta forma mais legível. Outra coisa sobre o comando if
em Haskell é que ele é uma expressão. Uma expressão é basicamente um pedaço de código que
retorna um valor. 5 é uma expressão porque isto retorna 5, 4 + 8 é uma expressão, x + y é uma
expressão porque retorna a soma de x e y. Por causa do else ser obrigatório, um if sempre
retornará alguma coisa porque ele é uma expressão. Se quisermos adicionar 1 para cada número
que será produzido na nossa função anterior, podemos escrevê­la da seguinte forma.
doubleSmallNumber' x = (if x > 100 then x else x*2) + 1  
Se tivéssemos omitido os parênteses ele teria adicionado 1 somente se x não fosse maior do que 100. Note o ' no final do
nome da função. Aquela apóstrofe tem nenhum significado especial na sintaxe do Haskell. Ele é um caracter válido para ser
usado em um nome de função. Normalmente nós utilizamos o ' para designar uma versão especifica de uma função
(aqueles que não são preguiçosos) ou em uma versão levemente modificada de uma função ou variável. Pelo fato do ' ser
um caractere válido em funções, nós podemos fazer uma função como esta:
conanO'Brien = "Este sou eu, Conan O'Brien!"   
Há duas observações bem interessantes aqui. A primeira é que no nome da função nós não podemos escrever o nome
próprio Conan's em letras maiúsculas. Isto porque funções não podem começar com letras maiúsculas. Veremos o porque
disto mais adiante. A segunda observação é que esta função não tem nenhum parâmetro. Quando uma função não tem
nenhum parâmetro, nós normalmente chamamos isto de definição (ou um nome). Como não podemos alterar o que os
nomes (e funções) significam após termos os definido, conanO'Brien e a string "Este sou eu, Conan O'Brien!" podem
ser usadas alternadamente.
Uma introdução às listas
Assim como as listas de compras no mundo real, listas em Haskell são extremamente úteis. Esta é
a estrutura de dados mais utilizada e pode ser utilizada em muitas e variadas formas para modelar
e resolver uma série de problemas. Listas são MUITO legais. Neste capitulo nós iremos dar uma
olhada no básico de listas, strings (que são listas) e compreensões de listas.
Em Haskell, listas são estruturas de dados homogêneas. Ela armazena vários elementos do mesmo tipo. Isto quer dizer que
podemos ter uma lista com inteiros ou uma lista de caracteres, porém não podemos ter uma lista com inteiros e alguns
caracteres. E agora, uma lista!
07/04/2016 Começando ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/starting­out 6/16
Nota: Nós podemos utilizar a palavra­chave let para definir o nome correto no GHCi. Fazer let a = 1 dentro do GHCi é
o equivalente a escrever a = 1 em um script e carregá­lo.
ghci> let lostNumbers = [4,8,15,16,23,48]  
ghci> lostNumbers  
[4,8,15,16,23,48]  
Como você pode ver, listas são caracterizadas por utilizar colchetes e os valores nas listas são separados por vírgulas. Se
nós tentarmos uma lista como [1,2,'a',3,'b','c',4], Haskell irá reclamar que os caracteres (que são, alias, demarcados
entre aspas simples) não são números. Falando em caracteres, strings são simplesmente listas de caracteres . "hello" é só
um açúcar sintático para ['h','e','l','l','o']. Como strings são listas, nós podemos utilizar funções de listas nelas, o
que é realmente útil.
Uma prática comum é colocar duas listas juntas. Fazemos isso utilizando o operador ++.
ghci> [1,2,3,4] ++ [9,10,11,12]  
[1,2,3,4,9,10,11,12]  
ghci> "hello" ++ " " ++ "world"  
"hello world"  
ghci> ['w','o'] ++ ['o','t']  
"woot"  
Veremos repetidamente o uso do operador ++ em strings grandes. Quando juntamos duas listas (mesmo se você adicionar
uma lista de um elemento só em outra lista, como por exemplo:[1,2,3] ++ [4]), internamente o Haskell irá percorrer toda a
lista que esta do lado esquerdo do ++. Isto não é um problema quando não estamos lidando com listas muito grandes. Porém
colocar algo no final de uma lista com cinqüenta milhões de elementos irádemorar um pouco. No entanto, colocar alguma
coisa no início de uma lista utilizando o operador : (também chamado de contra operador) será instantâneo.
ghci> 'Q':" GATINHA"  
"Q GATINHA"  
ghci> 5:[1,2,3,4,5]  
[5,1,2,3,4,5]  
Observe que o : recebe um número e uma lista de números ou um caractere e uma lista de caracteres, enquanto o ++
recebe duas listas. Mesmo que você esteja adicionando um elemento ao final de uma lista com ++, você terá que demarcá­la
com colchetes para que se torne lista.
[1,2,3] é na verdade açúcar sintático para 1:2:3:[]. [] é uma lista vazia. Se acrescentarmos 3 nele, ele irá se tornar [3].
Se acrescentarmos 2, se tornará [2,3] e assim por diante.
Nota: [], [[]] e [[],[],[]] são coisas diferentes. O primeiro é uma lista vazia, o segundo é uma lista que contém uma
lista vazia, o terceiro é uma lista que contém três listas vazias.
Se você deseja obter um elemento de uma lista pelo seu índice, utilize !!. O índice inicia a partir de 0.
ghci> "Steve Buscemi" !! 6  
'B'  
ghci> [9.4,33.2,96.2,11.2,23.25] !! 1  
33.2  
07/04/2016 Começando ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/starting­out 7/16
Mas se você tentar obter o sexto elemento de uma lista que contém somente quatro elementos, você obterá um erro portanto
seja cuidadoso!
Listas também podem conter listas. Elas também podem conter listas que contêm listas que contêm listas...
ghci> let b = [[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3]]  
ghci> b  
[[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3]]  
ghci> b ++ [[1,1,1,1]]  
[[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3],[1,1,1,1]]  
ghci> [6,6,6]:b  
[[6,6,6],[1,2,3,4],[5,3,3,3],[1,2,2,3,4],[1,2,3]]  
ghci> b !! 2  
[1,2,2,3,4]   
As listas dentro de uma lista podem conter lengths diferentes, mas eles não podem ser de tipos diferentes. Assim como você
não pode ter uma lista que tem alguns caracteres e alguns números, você não pode ter uma lista que contém algumas listas
de caracteres e algumas listas de números.
As listas poderão ser comparadas se as coisas que elas contêm puderem ser comparadas. Quando utilizamos <, <=, > e >=
para comparar listas, eles são comparados na ordem lexicográfica. Primeiro os cabeçalhos são comparados. Se eles forem
iguais então os segundos elementos são comparados, etc.
ghci> [3,2,1] > [2,1,0]  
True  
ghci> [3,2,1] > [2,10,100]  
True  
ghci> [3,4,2] > [3,4]  
True  
ghci> [3,4,2] > [2,4]  
True  
ghci> [3,4,2] == [3,4,2]  
True  
O que mais você pode fazer com listas? Aqui estão algumas funções básicas para operações em listas.
head  recebe uma lista e retorna o seu head. O head (cabeça) de uma lista é basicamente o primeiro elemento.
ghci> head [5,4,3,2,1]  
5   
tail  recebe uma lista e retorna a sua "cauda". Em outras palavras, ele decepa a cabeça de uma lista e retorna a cauda.
ghci> tail [5,4,3,2,1]  
[4,3,2,1]   
last  recebe uma lista e retorna o seu último elemento.
ghci> last [5,4,3,2,1]  
1   
07/04/2016 Começando ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/starting­out 8/16
init  recebe uma lista e retorna tudo com exceção do último elemento.
ghci> init [5,4,3,2,1]  
[5,4,3,2]   
Se pensarmos em uma lista como um monstro, aqui está o que é o que.
Mas o que acontece se tentarmos obter o head de uma lista vazia?
ghci> head []  
*** Exception: Prelude.head: empty list  
WOW! Tudo explode bem na nossa cara! Isso não é um mostro, não tem cabeça! Quando for utilizar head, tail, last e
init, tenha o cuidado para não utilizar em listas vazias. Este erro não pode ser capturado em tempo de compilação por isso
é sempre bom tomar certos cuidados antes de pedir para o Haskell lhe dar algum elemento de uma lista vazia.
length  recebe uma lista e retorna o seu length (tamanho), obviamente.
ghci> length [5,4,3,2,1]  
5  
null  verifica se a lista é vazia. Se for, então retorna True, senão retorna False. Utilize esta função no lugar de xs == []
(Caso você tiver uma lista chamada xs)
ghci> null [1,2,3]  
False  
ghci> null []  
True  
reverse  reverte uma lista.
ghci> reverse [5,4,3,2,1]  
[1,2,3,4,5]  
07/04/2016 Começando ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/starting­out 9/16
take  recebe um número e uma lista. Ele extrai a quantidade de elementos desde o início da lista.. Observe.
ghci> take 3 [5,4,3,2,1]  
[5,4,3]  
ghci> take 1 [3,9,3]  
[3]  
ghci> take 5 [1,2]  
[1,2]  
ghci> take 0 [6,6,6]  
[]  
Observe que se tentarmos tirar mais elementos do que há na lista, ele retorna só a lista. Se nós tentarmos retornar 0
elementos, receberemos uma lista vazia.
drop  funciona de forma similar, só que retira o número de elementos a partir do ínicio da lista.
ghci> drop 3 [8,4,2,1,5,6]  
[1,5,6]  
ghci> drop 1 [7,6,5,4]  
[6,5,4]  
ghci> drop 0 [1,2,3,4]  
[1,2,3,4]  
ghci> drop 100 [1,2,3,4]  
[]   
maximum  recebe uma lista de coisas que podem ser colocadas em algum tipo de ordem e retorna o seu maior elemento.
minimum  retorna o menor.
ghci> minimum [8,4,2,1,5,6]  
1  
ghci> maximum [1,9,2,3,4]  
9   
sum  recebe uma lista de números e retorna a sua soma.
product  recebe uma lista de números e retorna o seu produto.
ghci> sum [5,2,1,6,3,2,5,7]  
31  
ghci> product [6,2,1,2]  
24  
ghci> product [1,2,5,6,7,9,2,0]  
0   
elem  recebe alguma coisa e uma lista de coisas e nos diz se esta coisa é um elemento da lista. Geralmente é chamado
como uma função infixa porque é mais fácil de ler dessa maneira.
ghci> 4 `elem` [3,4,5,6]  
True  
ghci> 10 `elem` [3,4,5,6]  
False  
07/04/2016 Começando ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/starting­out 10/16
Estas são algumas funções básicas para operações em listas. Iremos dar mais uma olhada em funções de listas depois.
Texas ranges
E se você precisar de uma lista com todos os números entre 1 e 20? Claro, iríamos
digitando todos eles, porém obviamente esta não seria uma solução para cavalheiros que
desejam excelência em sua linguagem de programação. Em função disso, nós utilizamos
ranges. Ranges é um jeito de construir listas que são uma seqüência aritmética de
elementos devidamente enumerados. Números podem ser enumerados. Um, dois, três,
quatro, etc. Caracteres também podem ser enumerados. O alfabeto é uma enumeração de
caracteres de A a Z. Nomes também podem ser enumerados. O que vem depois de
"João"? Não faço idéia.
Para constituir uma lista que contenha todos os números naturais de 1 a 20, você
simplesmente deve escrever [1..20]. Isto é o equivalente a ter escrito
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20] e não há diferença entre
um e outro com a exceção de que escrever longas seqüências enumeradas manualmente é algo bastante estúpido.
ghci> [1..20]  
[1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20]  
ghci> ['a'..'z']  
"abcdefghijklmnopqrstuvwxyz"  
ghci> ['K'..'Z']  
"KLMNOPQRSTUVWXYZ"   
Ranges também são legais porque você pode definir uma etapa específica. E se nós quisermos todos os números ímpares
entre 1 e 20? Ou se nós quisermos sempre ter o terceiro número entre 1 e 20?
ghci> [2,4..20]  
[2,4,6,8,10,12,14,16,18,20]  
ghci> [3,6..20]  
[3,6,9,12,15,18]   
Esta é uma questão simples de separar os primeiros dois elementos com uma vírgula e então especificar qual o limite
máximo. Embora bastante esperto, ranges com diversas etapas não são tão espertos como muitas pessoas esperam que
eles sejam. Você não pode fazer [1,2,4,8,16..100] e esperar que isso lhe retorne a potência de 2. Primeiramente porque
você pode especificar somente uma etapa. E segundo que algumas seqüências não são aritméticas, mas sim ambíguas se
tivermos somente alguns dos primeiros termos.
Para ter uma lista com todos os números entre 20 e 1, você não pode simplesmentefazer [20..1], você deve fazer
[20,19..1].
Veja como utilizamos números ponto flutuante em ranges! Como eles não são completamente precisos (por definição), o uso
deles em ranges pode retornar alguns resultados que cheiram bem mal.
ghci> [0.1, 0.3 .. 1]  
07/04/2016 Começando ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/starting­out 11/16
[0.1,0.3,0.5,0.7,0.8999999999999999,1.0999999999999999]  
Meu conselho é que não se faça uso deles em ranges de listas.
Você também pode utilizar ranges para fazer listas infinitas, basta não especificar qual o limite máximo. Depois nós iremos
adentrar com mais detalhes nas listas infinitas. No momento, vamos examinar como que você pode ter os primeiros 24
múltiplos de 13. Claro, você quer fazer [13,26..24*13]. Porém eis aqui um jeito melhor: take 24 [13,26..]. Como Haskell
é preguiçoso, ele não vai tentar analisar uma lista infinita imediatamente porque isto nunca vai acabar. Ele vai ficar esperando
pra ver no que esta lista infinita vai dar. E aqui ele vê que você quer apenas os primeiros 24 elementos e de bom grado lhe
retorna eles.
ghci> take 24 [13,26..]  
[13,26,39,52,65,78,91,104,117,130,143,156,169,182,195,208,221,234,247,260,273,286,299,312]  
Algumas funções para produzirmos listas infinitas:
cycle  recebe uma lista e gera ciclos infinitos dela. Se você tentar mostrar o resultado, continuará para sempre até que você
tente cortá­lo fora em algum lugar.
ghci> take 10 (cycle [1,2,3])  
[1,2,3,1,2,3,1,2,3,1]  
ghci> take 12 (cycle "LOL ")  
"LOL LOL LOL "   
repeat  recebe um elemento e produz uma lista infinita dele. Isto é como o ciclo de uma lista com somente um elemento.
ghci> take 10 (repeat 5)  
[5,5,5,5,5,5,5,5,5,5]  
No entanto será mais simples utilizar a função replicate caso você deseje algumas repetições do mesmo elemento em uma
lista. replicate 3 10 retornará [10,10,10].
Eu sou a compreensão de lista
Se você já esteve em um curso de matemática, você provavelmente já viu compreensão de
conjuntos. Isto é normalmente utilizado para a construção de conjuntos mais específicos de
conjuntos em geral. Uma compreensão básica para um conjunto que contém os dez primeiros
números naturais seria  . A parte depois do pipe é
chamada de output da função, x é a variável, N é o input e x <= 10 o predicado. Isso significa
que o conjunto contém o dobro de todos os números naturais que satisfazem o predicado.
Se quisermos escrever isto em Haskell nós podemos fazer algo como take 10 [2,4..]. Mas se nós não quisermos dobrar
07/04/2016 Começando ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/starting­out 12/16
os 10 primeiros números naturais mas sim aplicar alguma função mais complexa neles? Nós podemos utilizar compreensão
de lista para isto. Compreensão de lista é bastante similar com compreensão de conjuntos. Vamos começar com os 10
primeiros números pares agora. A compreensão de lista que podemos usar é [x*2 | x <‐ [1..10]]. x é traçado a partir de
[1..10] e para cada elemento em [1..10] (que temos vinculado a x) teremos esse elemento dobrado. Aqui esta a
compreensão em ação.
ghci> [x*2 | x <‐ [1..10]]  
[2,4,6,8,10,12,14,16,18,20]  
Como você vê, obtemos os resultados desejados. Agora vamos adicionar uma condição (ou um predicado) nesta
compreensão. Predicados ficam depois dessa parte obrigatória sendo separado por vírgula. Digamos que nós queremos
somente os elementos que, dobrados, são maiores ou igual a 12.
ghci> [x*2 | x <‐ [1..10], x*2 >= 12]  
[12,14,16,18,20]  
Legal, isso funciona. E se nós quisermos todos os números entre 50 e 100 onde o resto da divisão pelo número 7 fosse 3?
Fácil.
ghci> [ x | x <‐ [50..100], x `mod` 7 == 3]  
[52,59,66,73,80,87,94]   
Sucesso! Note que reduzir as listas por predicados é chamado de filtragem. Nós temos uma lista de números e nós os
filtraremos pelo predicado. Vamos a outro exemplo. Digamos que você queira uma compreensão que substitua cada número
ímpar maior do que 10 com "BANG!" e cada número ímpar menor do que 10 com "BOOM!". Se o número não for ímpar,
queremos ele fora da lista. Convenientemente nós colocamos esta compreensão dentro de uma função para reutilizá­la mais
facilmente.
boomBangs xs = [ if x < 10 then "BOOM!" else "BANG!" | x <‐ xs, odd x]   
A última parte da compreensão é o predicado. A função odd retorna True quando o número for ímpar e False quando for
par. O elemento é incluído na lista somente se todos os predicados examinados sejam True.
ghci> boomBangs [7..13]  
["BOOM!","BOOM!","BANG!","BANG!"]   
Podemos também incluir vários predicados. Se nós quisermos todos os números entre 10 e 20 que não sejam 13, 15 ou 19,
podemos fazer:
ghci> [ x | x <‐ [10..20], x /= 13, x /= 15, x /= 19]  
[10,11,12,14,16,17,18,20]  
Não apenas podemos ter vários predicados em uma compreensão de lista (um elemento deve satisfazer todos os predicados
para ser incluso no resultado da lista), como também podemos extrair várias listas. Quando extraímos diversas listas, a
compreensão produz todas as combinações da lista desejada e junta com a função de output que nós fornecemos. Uma lista
produzida pela compreensão da extração a partir de duas listas com length 4 terá um length de 16, desde que não seja
07/04/2016 Começando ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/starting­out 13/16
filtrado. Se eu tiver duas listas, [2,5,10] e [8,10,11] e quiser obter o produto de todas as combinações possíveis entre
números destas listas, aqui esta o que devemos fazer.
ghci> [ x*y | x <‐ [2,5,10], y <‐ [8,10,11]]  
[16,20,22,40,50,55,80,100,110]   
Como esperado, o length da nova lista é 9. E se eu quiser todos os possíveis produtos que sejam maiores do que 50?
ghci> [ x*y | x <‐ [2,5,10], y <‐ [8,10,11], x*y > 50]  
[55,80,100,110]   
Que tal uma compreensão de lista que combina uma lista de adjetivos e uma lista de substantivos... só para dar umas
risadas.
ghci> let substantivos = ["gerente","programador","cliente"]  
ghci> let adjetivos = ["malemolente","chato","fofoqueiro"]  
ghci> [substantivos ++ " " ++ adjetivos | substantivos <‐ substantivos, adjetivos <‐ adjetivos]  
["gerente malemolente","gerente chato","gerente fofoqueiro","programador malemolente",  
"programador chato","programador fofoqueiro","cliente malemolente","cliente chato",  
"cliente fofoqueiro"]  
Já sei! Vamos escrever a nossa própria versão do length! Chamaremos isto de length'.
length' xs = sum [1 | _ <‐ xs]   
_ significa que não me importo com o que vamos extrair da lista, ao invés de escrever o nome da variável que nunca será
usada, basta escrever _. Esta função substitui todos os elementos da lista com 1 e soma todos eles. A idéia é ter o resultado
da soma e obter depois o length da nossa lista.
Lembrete de amigo: como strings são listas, nós devemos usar compreensão de listas para processar e produzir strings. Eis
uma função que recebe uma string e remove tudo exceto letras maiúsculas dela.
removeNonUppercase st = [ c | c <‐ st, c `elem` ['A'..'Z']]   
Testando isto:
ghci> removeNonUppercase "Hahaha! Ahahaha!"  
"HA"  
ghci> removeNonUppercase "EUnaoGOSTODEGATOS"  
"EUGOSTODEGATOS"   
O predicado aqui faz todo o serviço. Ele dirá ao caractere que ele será incluído em uma nova lista somente se ele for um
elemento da lista ['A'..'Z']. Aninhar compreensões de lista também é possível se você estiver operando em uma lista que
contém listas. Uma lista que contém diversas listas de números. Vamos remover todos os números ímpares sem diminui­los.
ghci> let xxs = [[1,3,5,2,3,1,2,4,5],[1,2,3,4,5,6,7,8,9],[1,2,4,2,1,6,3,1,3,2,3,6]]  
ghci> [ [ x | x <‐ xs, even x ] | xs <‐ xxs]  
[[2,2,4],[2,4,6,8],[2,4,2,6,2,6]]  
07/04/2016 Começando ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/starting­out14/16
Você pode escrever compreensão de lista em diversas linhas. Se você não estiver no GHCI, é melhor que quebre longas
compreensões de lista em multiplas linhas, especialmente se forem aninhadas.
Tuplas
Em alguns casos, tuplas são como listas — um jeito de armazenar muitos valores em um lugar
só. No entanto, existem algumas diferenças fundamentais. Uma lista de números é uma lista de
números. Esse é o seu tipo e não importa se tiver um único número nela ou uma quantidade
infinita de números. Tuplas, no entanto, são usadas quando você sabe exatamente quantos
valores você quer combinar e o seu tipo depende de quantos componentes ela tem e os tipos
dos componentes. Tuplas são caracterizadas por parênteses com seus componentes separados
por vírgulas.
Outra diferença fundamental é que elas não precisam ser homogêneas. Ao contrário de uma lista, uma tupla pode conter
uma combinação de vários tipos.
Pense em como você representaria um vetor bi­dimensional em Haskell. Um jeito seria você usar uma lista. Esse seria um
jeito de fazer. E se você quiser colocar alguns vetores em uma lista para representar pontos de uma superfície plana em
duas dimensões? Você poderia fazer algo como [[1,2],[8,11],[4,5]]. O problema com este método é que podemos fazer
algumas coisas como [[1,2],[8,11,5],[4,5]], com Haskell não tem problema desde que isso seja uma lista de listas com
números, mas meio que não faz sentido. Mas uma tupla de tamanho dois (também chamado de um par) é o seu próprio tipo,
sabendo disto aquela lista não pode ter alguns pares e depois umas triplas (uma tupla de tamanho três), mas vamos utilizar
mesmo assim. Em vez de contornar os vetores com colchetes, usamos parênteses: [(1,2),(8,11),(4,5)] E se eu quiser
uma superfície como [(1,2),(8,11,5),(4,5)]? Bem, isto me dará um erro:
Couldn't match expected type `(t, t1)'  
against inferred type `(t2, t3, t4)'  
In the expression: (8, 11, 5)  
In the expression: [(1, 2), (8, 11, 5), (4, 5)]  
In the definition of `it': it = [(1, 2), (8, 11, 5), (4, 5)]  
Ele esta me dizendo que tentei usar um par e um triplo na mesma lista, isso não pode acontecer. Você não pode fazer uma
lista como [(1,2),("One",2)] porque o primeiro elemento da lista é um par de números e o segundo elemento é um par
que consiste em uma string e um número. Tuplas podem também ser usadas para representar uma variedade de dados. Por
exemplo, se eu quiser representar alguns nomes e idades em Haskell, eu poderia utilizar uma tripla:
("Christopher", "Walken", 55). Como vimos nesse exemplo, tuplas também podem conter listas.
Utilize tuplas quando você souber com antecedência que muitos componentes contêm algumas partes de dados já
esperadas. Tuplas são muito mais rígidas porque cada diferença de tamanho da tupla é o seu próprio tipo, você não pode
escrever uma função geral para anexar elementos para uma tupla ­ você deve escrever uma função para anexar elementos a
um par, uma função para anexar para uma tripla, uma função para uma 4­tuplas, etc.
Enquanto você tiver listas únicas, não existirá tal coisa como uma tupla única. Realmente não faz muito sentido quando você
pensa sobre isso. Uma tupla única seria apenas o valor que ele contém e, como tal, não teria nenhum benefício para nós.
Assim como as listas, tuplas podem ser comparadas umas com as outras se seus componentes puderem ser comparados.
Você só não pode comparar duas tuplas de tamanho diferente, uma vez que você pode comparar duas listas de tamanhos
diferentes. Duas funções úteis que operam em pares:
07/04/2016 Começando ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/starting­out 15/16
fst  recebe um par e retorna seu primeiro componente.
ghci> fst (8,11)  
8  
ghci> fst ("Wow", False)  
"Wow"  
snd  recebe um par e retorna seu segundo componente. Surpresa!
ghci> snd (8,11)  
11  
ghci> snd ("Wow", False)  
False  
Nota: estas funções operam somente em pares. Não funcionam com triplas, 4­tuplas, 5­tuplas, etc. Falaremos sobre a
extração de dados de tuplas de diferentes maneiras um pouco mais tarde.
Uma função legal que produz uma lista de pares:  zip . Ela pega duas listas e então as comprimem juntamente em uma
única lista juntando os elementos que casarem em pares. Esta é realmente uma função muito simples mas tem uma
infinidade de usos. Ela é bastante útil especialmente quando precisamos combinar duas listas em um único formato ou cruzar
duas listas simultaneamente. Veja uma demonstração:
ghci> zip [1,2,3,4,5] [5,5,5,5,5]  
[(1,5),(2,5),(3,5),(4,5),(5,5)]  
ghci> zip [1 .. 5] ["um", "dois", "tres", "quatro", "cinco"]  
[(1,"um"),(2,"dois"),(3,"tres"),(4,"quatro"),(5,"cinco")]  
Ela pareia todos os elementos e produz uma nova lista. O primeiro elemento vai com o primeiro, o segundo com o segundo,
etc. Observe que como pares podem ter tipos diferentes entre eles, zip pode pegar duas listas que contém tipos diferentes e
comprimir em uma só. O que acontece se os tamanhos das listas forem diferentes?
ghci> zip [5,3,2,6,2,7,2,5,4,6,6] ["eu","sou","uma", "tartaruga"]  
[(5,"eu"),(3,"sou"),(2,"uma"),(6,"tartaruga")]  
A lista maior é simplesmente cortada para combinar com o tamanho da outra menor. Como Haskell é preguiçoso, nós
podemos "zippar" listas finitas com listas infinitas:
ghci> zip [1..] ["banana", "laranja", "melancia", "uva"]  
[(1,"banana"),(2,"laranja"),(3,"melancia"),(4,"uva")]  
07/04/2016 Começando ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/starting­out 16/16
Aqui esta um problema para combinar tuplas e compreensão de listas: Que triângulo retângulo que tem números inteiros
para todos os lados e todos os lados iguais ou menores que 10 e tem um perímetro de 24? Primeiro, vamos tentar gerar
todos os triângulos com lados iguais ou menores que 10:
ghci> let triangles = [ (a,b,c) | c <‐ [1..10], b <‐ [1..10], a <‐ [1..10] ]   
Estamos apenas desenhando a partir de três listas e a nossa função de output irá combiná­los em uma tripla. Se você avaliar
isso digitando triangles no GHCI, você terá uma lista de todos os possíveis triângulos com os lados menores ou iguais a 10.
A seguir, iremos adicionar uma condição que todas elas têm de ser triângulos retângulos. Vamos também modificar esta
função levando em consideração que o lado b não é maior do que a hipotenusa e esse lado não é maior do que o lado b.
ghci> let rightTriangles = [ (a,b,c) | c <‐ [1..10], b <‐ [1..c], a <‐ [1..b], a^2 + b^2 == c^2]   
Estamos quase terminando. Agora, acabamos de modificar a função, dizendo que queremos aqueles em que o perímetro é
igual a 24.
ghci> let rightTriangles' = [ (a,b,c) | c <‐ [1..10], b <‐ [1..c], a <‐ [1..b], a^2 + b^2 == c^2, a+b+c
ghci> rightTriangles'  
[(6,8,10)]  
E cá esta nossa resposta! Este é um padrão comum em programação funcional. Você tem um conjunto inicial de soluções e
em seguida você aplica transformações a essas soluções e as filtram até chegar ao resultado correto.
Introdução Índice Tipos e Typeclasses
07/04/2016 Tipos e Typeclasses ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/types­and­typeclasses 1/8
Começando Índice Sintaxe em Funções
Tipos e Typeclasses
Acredite no tipo
Já falamos que Haskell possui um sistema de tipos estático. O tipo de toda expressão é
conhecido na hora da compilação, o que resulta num código mais seguro. Se você escrever
um programa que tente dividir um tipo booleano por um número, ele nem compilará. Isso é
bom porque é melhor detectarmos erros logo ao terminar de programar do que se deparar
com travamentos indesejados. Tudo em Haskell tem um tipo, então o compilador pode
considerar várias possibilidades antes mesmo de compilá­lo.
Ao contrário de Java ou Pascal, Haskell tem inferência de tipo. Se for digitado um número, não precisamos avisar ao Haskell
que é um número. Ele consegue identificarisso automaticamente, não damos os tipos de funções e expressões
explicitamente. Nós veremos apenas o básico de Haskell com uma visão apenas superficial sobre tipos. No entanto, entender
o sistema de tipos é muito importante para o aprendizado de Haskell.
Um "tipo" é algo como uma etiqueta que toda expressão têm, que nos diz em qual categoria ela se encaixa. A expressão
True é booleana, "hello" é uma string, etc.
Agora usaremos o GHCI para descobrir os tipos de algumas expressões. Faremos isso usando o comando :t que, seguido
de qualquer expressão válida, retorna o seu tipo. Vamos dar uma olhada.
ghci> :t 'a'  
'a' :: Char  
ghci> :t True  
True :: Bool  
ghci> :t "HELLO!"  
"HELLO!" :: [Char]  
ghci> :t (True, 'a')  
(True, 'a') :: (Bool, Char)  
ghci> :t 4 == 5  
4 == 5 :: Bool  
Fazendo isso, vemos que :t mostra a expressão seguida de :: e seu tipo. :: pode ser lido
como "do tipo". Tipos explícitos sempre são referidos pela sua primeira letra maiúscula. 'a',
como é visto, é do tipo Char. Não é difícil de identificar que vem da palavra caracter. True é
Boolean. Faz sentido. Mas, o que é isso? Examinando o tipo de "HELLO!" descobrimos que ele
é [Char]. Os colchetes denotam uma lista. Então lemos isso como uma lista de caracteres. Ao
contrário de listas, cada valor da tupla tem seu tipo. Então a expressão (True, 'a') tem o tipo
(Bool, Char), e uma expressão como ('a','b','c') deverá retornar (Char, Char, Char).
4 == 5 sempre retornará False, que é do tipo Bool.
07/04/2016 Tipos e Typeclasses ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/types­and­typeclasses 2/8
Funções também têm tipos. Quando escrevemos nossas próprias funções, podemos declarar explicitamente quais são os
seus tipos. Isso geralmente é considerado uma boa prática exceto quando a função é muito curta. Daqui pra frente daremos
vários exemplos que fazem uso de declaração de tipos. Você ainda se lembra daquela compreensão de lista que criamos no
capítulo anterior e que usava filtros para retornar somente a parte maiúscula de uma string? Então, aqui esta ela novamente
com os tipos declarados.
removeNonUppercase :: [Char] ‐> [Char]  
removeNonUppercase st = [ c | c <‐ st, c `elem` ['A'..'Z']]   
removeNonUppercase tem o tipo [Char] ‐> [Char], o que significa que ele parte de uma string e chega em outra string. Ou
seja, ele irá receber como parâmetro uma string e nos devolver outra string como resultado. O tipo [Char] é sinônimo de
String então ficaria mais claro se fosse escrito removeNonUppercase :: String ‐> String. Não precisariamos fazer essa
declaração de tipo para o compilador porque ele poderia inferir por si só que essa função recebe uma string e retorna outra
string. E se nós precisarmos de uma função que recebe vários tipos como parâmetro? Vamos ver então uma função bem
simples que pega três inteiros e os soma:
addThree :: Int ‐> Int ‐> Int ‐> Int  
addThree x y z = x + y + z  
Os parâmetros são separados por ‐> e não há nenhuma distinção entre tipos de parâmetros e retorno. O tipo do retorno é o
último e os parâmetros são os três primeiros. Mais adiante veremos o porquê deles serem apenas separados por um ‐> ao
invés de ter um destaque maior como Int, Int, Int ‐> Int ou algo do gênero.
Se você deseja especificar o tipo da função, mas não tem certeza de qual deve ser, você pode escrevê­la normalmente e
depois descobrir com o :t. Funções também são expressões, então :t funciona sem problemas.
Aqui temos um apanhado geral dos principais tipos.
Int  é inteiro. É usado por números inteiros. 7 pode ser um Int mas 7.2 não. Int possui limitações de tamanho, o que
significa ter um máximo e um mínimo. Geralmente os computadores de 32bit têm um Int máximo de 2147483647 e um
mínimo de ­2147483648.
Integer  significa, hmmm... inteiro também. A principal diferença é que não tem limitações e pode ser usado por números
realmente grandes. Digo, extremamente grandes. Contudo, Int é mais eficiente.
factorial :: Integer ‐> Integer  
factorial n = product [1..n]  
ghci> factorial 50  
30414093201713378043612608166064768844377641568960512000000000000  
Float  é um número real em ponto flutuante de precisão simples.
circumference :: Float ‐> Float  
circumference r = 2 * pi * r  
07/04/2016 Tipos e Typeclasses ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/types­and­typeclasses 3/8
ghci> circumference 4.0  
25.132742  
Double  é um número real em ponto flutuante com o dobro(!) de precisão.
circumference' :: Double ‐> Double  
circumference' r = 2 * pi * r  
ghci> circumference' 4.0  
25.132741228718345  
Bool  é um tipo booleano. Pode ter apenas dois valores: True ou False.
Char  representa um caractere. É delimitado por aspas simples. Uma lista de caracteres é denominada String.
Tuplas são tipos mas possuem uma variação de acordo com a quantidade e valores que contém, então teoricamente temos
tuplas com infinitos tipos, o que é demais para cobrir nesse tutorial. Note que uma tupla vazia  ()  é também um tipo e pode
assumir apenas um valor: ()
Tipo variável
Qual você acha que é o tipo da função head? Já que head recebe uma lista e retorna o seu último elemento, qual deve ser o
seu tipo? Vamos descobrir!
ghci> :t head  
head :: [a] ‐> a  
Hmmm! O que é esse a? É o tipo? Lembre­se que já vimos que o tipo é escrito com a primeira letra
maiúscula, então não pode ser exatamente o tipo. E é exatamente essa diferença que nos diz ser um
tipo variável. Isso significa que o a pode ser qualquer tipo. Isso é algo como os genéricos de outras
linguagens, mas em Haskell é muito mais poderoso porque nos permite facilmente escrever funções
mais genéricas caso o processamento seja o mesmo para diferentes tipos. Funções que possuem
tipos variáveis são denominadas funções polimórficas. A declaração de tipo em head diz que ele recebe uma lista de
elementos de qualquer tipo e retorna um elemento dela.
Embora tipos variáveis possam ter nomes com mais de um caractere, normalmente nós damos a eles nomes como a, b, c,
d...
Se lembra da função fst? Aquela que retorna o primeiro componente de um par? Então, vamos examinar o seu tipo:
ghci> :t fst  
fst :: (a, b) ‐> a  
Examinando o tipo de fst, a gente vê que ele recebe uma tupla que contém dois tipos e retorna o primeiro elemento. É
exatamente por causa disso que conseguimos usar fst em pares que contenham quaisquer tipos. Perceba ainda que
mesmo a e b sendo diferentes tipos variáveis, eles não devem ser necessariamente de tipos diferentes. A declaração apenas
nos diz que o tipo do primeiro componente (da tupla) deve ser do mesmo que o do retorno.
07/04/2016 Tipos e Typeclasses ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/types­and­typeclasses 4/8
Basicão de Typeclasses
Uma Typeclass (classe de tipos) é como uma interface que define um comportamento.
Se um tipo é parte de uma typeclass, quer dizer que ela suporta e implementa o
comportamento especificado pela classe de tipo. Muita gente vinda da orientação a
objetos se confunde e acha estar diante de uma classe de OO. Bom... não. Você pode
pensar que são como as interfaces de Java, mas na verdade são muito melhor.
Qual deve ser o tipo da função ==?
ghci> :t (==)  
(==) :: (Eq a) => a ‐> a ‐> Bool  
Nota: o operador de igualdade (==) é uma função. Assim como +, *, ‐, / e quase todos os outros operadores. Se uma
função é composta apenas de caracteres especiais, ela é por padrão uma função infixa. Se quisermos verificar o seu tipo,
passe­a para outra função ou chame­a como função prefixa, colocando­a entre parênteses.
Interessante. Temos algo novo aqui, o símbolo =>. Tudo antes do símbolo => é denominado class constraint (restrição de
classe). Podemos ler a declaração de tipo anterior assim: a função de igualdade recebe dois argumentos de mesmo tipoe
retorna um Bool. Esse tipo deve ser membro da classe Eq (que é a class constraint).
A typeclass Eq provê uma interface para o teste de igualdade. Qualquer tipo que faça sentido ser verificado por igualdade
com outro tipo deve estar na typeclass Eq. Todos os tipos Haskell ­ exceto os de IO (tipo para lidar com entrada e saída) e
funções ­ fazem parte da typeclass Eq.
A função elem tem o tipo (Eq a) => a ‐> [a] ‐> Bool porque usa o operador == para procurar um determinado elemento
em uma dada lista.
Algumas classes de tipo básicas:
Eq  é usado por tipos que suportam teste por igualdade. As funções que fazem parte dela implementam == e /=. Se existe
alguma class constraint de Eq para um tipo variável em uma função, usa o operador == ou /= em algum lugar de sua
definição. Todos os tipos já mencionados (com excessão de funções), são parte de Eq, então podem ser testados por
igualdade.
ghci> 5 == 5  
True  
ghci> 5 /= 5  
False  
ghci> 'a' == 'a'  
True  
ghci> "Ho Ho" == "Ho Ho"  
True  
ghci> 3.432 == 3.432  
True  
Ord  é para tipos que têm ordem.
ghci> :t (>)  
07/04/2016 Tipos e Typeclasses ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/types­and­typeclasses 5/8
(>) :: (Ord a) => a ‐> a ‐> Bool  
Todos os tipos já vistos (exceto funções) são parte de Ord. Ord engloba todas as funções de comparação comuns como >, <,
>= e <=. A função compare requer dois membros de Ord de mesmo tipo e retorna sua ordenação.  Ordering  é uma
typeclass que pode ser GT, LT ou EQ, significando maior que, menor que e igual a, respectivamente.
Para ser membro de Ord, um tipo deve ser membro do prestigioso e restrito clube do Eq.
ghci> "Abrakadabra" < "Zebra"  
True  
ghci> "Abrakadabra" `compare` "Zebra"  
LT  
ghci> 5 >= 2  
True  
ghci> 5 `compare` 3  
GT  
Membros do  Show  podem ser representados como strings. Todos os tipos cobertos até agora (com exceção das funções)
são suportados por Show. A função que lida com a typeclass Show mais usada é a show. Ela recebe um valor de um que tipo
presente em Show e nos mostra esse valor como uma string.
ghci> show 3  
"3"  
ghci> show 5.334  
"5.334"  
ghci> show True  
"True"  
Read  é tipo uma oposição da typeclass Show. A função read recebe uma string e retorna um tipo membro de Read.
ghci> read "True" || False  
True  
ghci> read "8.2" + 3.8  
12.0  
ghci> read "5" ‐ 2  
3  
ghci> read "[1,2,3,4]" ++ [3]  
[1,2,3,4,3]  
Até agora tudo simples. Todos os tipos já vistos estão nessas classes de tipo. Mas o que acontece ao tentarmos read "4"?
ghci> read "4"  
<interactive>:1:0:  
    Ambiguous type variable `a' in the constraint:  
      `Read a' arising from a use of `read' at <interactive>:1:0‐7  
    Probable fix: add a type signature that fixes these type variable(s)  
O que o GHCI está tentando nos dizer é que não sabe o que se esperar como retorno. Perceba que nos usos anteriores de
read nós sempre fazíamos algo com o resultado. Assim, o GHCI podia inferir o tipo esperado de read. Se usassemos ele
como um booleano, ele saberia que deveria retornar um Bool. Mas agora ele só sabe que deve ser algum tipo da classe
Read. Vamos dar uma olhada na declaração de tipo de read.
ghci> :t read  
07/04/2016 Tipos e Typeclasses ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/types­and­typeclasses 6/8
read :: (Read a) => String ‐> a  
Viu? Ele retorna um tipo parte de Read mas como não usamos o resultado depois, ele não saberá qual tipo será. É por isso
que podemos especificar explicitamente type annotations (anotações de tipos). Anotações de tipos servem para dizer qual
tipo que você quer que uma expressão assuma. Fazemos isso adicionando :: no fim da expressão com o tipo desejado.
Observe:
ghci> read "5" :: Int  
5  
ghci> read "5" :: Float  
5.0  
ghci> (read "5" :: Float) * 4  
20.0  
ghci> read "[1,2,3,4]" :: [Int]  
[1,2,3,4]  
ghci> read "(3, 'a')" :: (Int, Char)  
(3, 'a')  
Na maioria das expressões, o compilador já pode assumir qual deve ser o tipo das expressões. Mas acontece dele não saber
se deve ser Int ou Float para uma expressão como read "5". Para ter certeza, Haskell deveria primeiro avaliar read "5".
Mas como Haskell é uma linguagem estaticamente tipada, precisa saber o tipo de todas as expressões na hora da
compilação (ou no caso do GHCI, interpretação). Então dizemos ao Haskell: "Ei, essa expressão é desse tipo, caso não
saiba!".
Os membros de  Enum  são tipos que possuem uma seqüência. A maior vantagem da typeclass Enum é poder ser usada em
ranges de listas. Seus tipos têm sucessores e predecessores definidos, que podem ser conseguidos pelas funções succ e
pred. Fazem parte dessa classe os tipos: (), Bool, Char, Ordering, Int, Integer, Float e Double.
ghci> ['a'..'e']  
"abcde"  
ghci> [LT .. GT]  
[LT,EQ,GT]  
ghci> [3 .. 5]  
[3,4,5]  
ghci> succ 'B'  
'C'  
Bounded  são os tipos que possuem limites ­ máximo e mínimo.
ghci> minBound :: Int  
‐2147483648  
ghci> maxBound :: Char  
'\1114111'  
ghci> maxBound :: Bool  
True  
ghci> minBound :: Bool  
False  
minBound e maxBound são diferenciados por ter tipo (Bounded a) => a. São constantes polimórficas.
Todas tuplas não­vazias também estão em Bounded.
ghci> maxBound :: (Bool, Int, Char)  
(True,2147483647,'\1114111')  
07/04/2016 Tipos e Typeclasses ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/types­and­typeclasses 7/8
Num  é uma typeclass numérica. Seus membros têm a função de agir como números. Vamos ver o tipo de um número.
ghci> :t 20  
20 :: (Num t) => t  
Parece que todos os números são constantes polimórficas. Elas podem tomas a forma de qualquer tipo da typeclass Num.
ghci> 20 :: Int  
20  
ghci> 20 :: Integer  
20  
ghci> 20 :: Float  
20.0  
ghci> 20 :: Double  
20.0  
Esses são os tipos da typeclass Num. Se verificar o tipo de *, descobrirá que ela aceita qualquer número.
ghci> :t (*)  
(*) :: (Num a) => a ‐> a ‐> a  
Recebe três números do mesmo tipo. É por isso que (5 :: Int) * (6 :: Integer) resultará em erro e
5 * (6 :: Integer) funcionará e retornará um Integer, já que 5 pode tomar a forma de um Int ou de um Integer.
Para estar em Num, o tipo já deve estar em Show e Eq.
Integral  também é uma typeclass numérica. Enquanto Num inclui todos os números (reais e inteiros), Integral apenas
inteiros. Essa typeclass é composta por Int e Integer.
Floating  inclui apenas números de ponto flutuante, então são Float e Double.
Uma função muito útil para lidar com números é  fromIntegral . A declaração do seu tipo é
fromIntegral :: (Num b, Integral a) => a ‐> b. Assim, vemos que ela recebe um número inteiro e transforma­o em
algo mais genérico. Isso é útil quando você precisa que tipos inteiros e ponto flutuante trabalhem juntos. Por exemplo, a
função length tem uma declaração de length :: [a] ‐> Int ao invés de ter algo mais geral como
(Num b) => length :: [a] ‐> b. Acho que está assim por razões históricas, o que, na minha opinião, é besteira. Ainda
assim, se tentarmos somar o tamanho de uma lista (length) com 3.2 teremos um erro, pois não é possível somar um Int
com um número de ponto flutuante. Então para contornar, fromIntegral (length [1,2,3,4]) + 3.2 funciona
perfeitamente.
Note que fromIntegral tem mais de um class constraint em sua declaração de tipo. Como pode ver, isso é válido, desde
que estejam separados por vírgulas dentro de parênteses.
Começando Índice Sintaxe em Funções
07/04/2016 Tipos e Typeclasses ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/types­and­typeclasses 8/8
07/04/2016 Sintaxe em Funções ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/syntax­in­functions 1/11
Tipos e Typeclasses Índice Recursão
Sintaxe emFunções
Pattern matching
Este capítulo irá cobrir algumas construções sintáticas interessantes do Haskell, e começaremos
por pattern matching. Pattern matching consiste na pesquisa por padrões em determinados
dados e, caso tenha sucesso, fazer algo com ele.
Ao definir funções, você pode definir códigos específicos para cada padrão. Isso gera um código
mais conciso, simples e legível. Você pode avaliar qualquer tipo de dado — números, caracteres,
listas, tuplas... Vamos fazer uma função bem simples que verifica se o número dado é sete ou
não.
lucky :: (Integral a) => a ‐> String  
lucky 7 = "SETE! BINGO!"  
lucky x = "Desculpe, tente novamente!"   
Ao chamar lucky, os padrões serão testados de cima para baixo e, de acordo com o resultado, executado ou não seu corpo.
Aqui, o único modo dessa pattern ter sucesso é se ela for 7. Senão, vamos para a segunda, que será executada de qualquer
maneira devido ao x. Essa função ainda poderia ser implementada por uma estrutura if. Mas e se quisermos uma função que
diga o número se o parâmetro for de 1 até 5 ou "Não está entre 1 e 5" para outros? Sem pattern matching, teríamos que
escrever uma estrura if then else bem confusa. No entanto, com ele podemos escrever assim
sayMe :: (Integral a) => a ‐> String  
sayMe 1 = "Um!"  
sayMe 2 = "Dois!"  
sayMe 3 = "Três!"  
sayMe 4 = "Quatro!"  
sayMe 5 = "Cinco!"  
sayMe x = "Não está entre 1 e 5"  
Veja que se movessemos a última pattern (o else geral) para o topo, sempre diria "Não está entre 1 e 5", porque seria
sempre executado para qualquer número e as linhas seguintes não teriam oportunidade de serem executadas.
Lembra da função fatorial que implementamos anteriormente? Definimos o fatorial de um número n como sendo
product [1..n]. Nós ainda podemos usar recursividade, que é como geralmente é definida na matemática. Começamos
explicitando que o fatorial de 0 é 1. Então dizemos que o fatorial de qualquer número inteiro positivo é ele multiplicado pelo
fatorial do predecessor. Assim fica traduzido para o idioma Haskell.
factorial :: (Integral a) => a ‐> a  
factorial 0 = 1  
factorial n = n * factorial (n ‐ 1)  
07/04/2016 Sintaxe em Funções ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/syntax­in­functions 2/11
Essa é a primeira vez que definimos uma função recursiva. Resursão é importante em Haskell e iremos ver com calma mais
tarde. Mas para dar água na boca, veremos agora o que acontece ao tentarmos calcular o fatorial de, digamos, 3: tentamos
descobrir o resultado de 3 * factorial 2. O fatorial de 2 é 2 * factorial 1, então até agora temos
3 * (2 * factorial 1). factorial 1 é 1 * factorial 0, então 3 * (2 * (1 * factorial 0)). É ai que temos o
truque — definimos o fatorial de 0 como sendo 1 com a intenção de não executar a terceira linha, então só retornamos 1. O
resultado final será o equivalente a 3 * (2 * (1 * 1)). Se tivéssemos colocado a linha para a recursão como primeira da
função, o zero seria incluido e o cálculo seria infinito. Por isso a ordem é muito importante ao definir patterns e é melhor
colocar as mais específicas no início e deixar as gerais para o fim.
Pattern matching também podem falhar, como no caso de definirmos uma função como essa:
charName :: Char ‐> String  
charName 'a' = "Albert"  
charName 'b' = "Broseph"  
charName 'c' = "Cecil"  
e tentarmos chamá­la com um parâmetro inesperado:
ghci> charName 'a'  
"Albert"  
ghci> charName 'b'  
"Broseph"  
ghci> charName 'h'  
"*** Exception: tut.hs:(53,0)‐(55,21): Non‐exhaustive patterns in function charName  
Isso acontece por não termos uma pattern genérica. Ao criar patterns, sempre devemos incluir pattern que cubra todas as
excessões para que o programa não pare de modo indevido caso usemos um valor inesperado.
Pattern matching também pode ser usada em tuplas. Se precisamos fazer uma função que recebe dois vetores em um plano
bidimensional (em forma de pares) e o some, somamos seus x primeiro e depois seus y. Assim seria como faríamos se não
conhecessemos pattern matching:
addVectors :: (Num a) => (a, a) ‐> (a, a) ‐> (a, a)  
addVectors a b = (fst a + fst b, snd a + snd b)  
Bom, funciona. Mas existe um modo muito melhor de se fazer a mesma coisa. Vamos modificar essa função para que passe
a usar pattern matching.
addVectors :: (Num a) => (a, a) ‐> (a, a) ‐> (a, a)  
addVectors (x1, y1) (x2, y2) = (x1 + x2, y1 + y2)  
Aí está. Muito melhor. Veja que temos um pattern padrão. O tipo de addVectors (em ambos casos) é
addVectors :: (Num a) => (a, a) ‐> (a, a) ‐ > (a, a), então garantimos que receberemos dois pares como
parâmetros.
fst e snd também precisam de parâmetros em pares. Mas e com triplas? Não há nenhuma função que trabalhe com elas,
mas podemos fazer a nossa própria.
07/04/2016 Sintaxe em Funções ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/syntax­in­functions 3/11
first :: (a, b, c) ‐> a  
first (x, _, _) = x  
  
second :: (a, b, c) ‐> b  
second (_, y, _) = y  
  
third :: (a, b, c) ‐> c  
third (_, _, z) = z  
_ significa o mesmo que em compreensão de listas. Se o compilador não deve se importar com o que há ali, usamos o _.
O que me lembra que patterns matching também podem ser usadas em compreensão de listas. Veja...
ghci> let xs = [(1,3), (4,3), (2,4), (5,3), (5,6), (3,1)]  
ghci> [a+b | (a,b) <‐ xs]  
[4,7,6,8,11,4]   
Se a pattern falhar, se moverá para o próximo elemento.
As prórias listas podem ser usadas em pattern matching. Você pode casar um padrão com uma lista vazia [] ou qualquer
padrão que involva : e uma lista vazia. Sabendo que [1,2,3] é apenas um açucar sintático para 1:2:3:[], você também
pode utilizar o padrão original. Uma pattern como x:xs irá colocar a cabeça de uma lista em x o resto dela em xs, ainda que
o único elemento em xs seja uma lista vazia.
Nota: O padrão x:xs é muito usado, principalmente em funções recursivas. O (possível) problema é de : funcionar
apenas com lista de length 1 ou maior.
Se você quer deixar, digamos, os três primeiros elementos de uma lista numa variável e o resto em outra, você pode
escrever algo como x:y:z:zs. Só funcionará com lista de 3 ou mais elementos.
Agora que conseguimos comparar um pattern com uma lista, vamos criar nossa própria implementação da função head.
head' :: [a] ‐> a  
head' [] = error "Proibido chamar head em uma lista vazia, amador!"  
head' (x:_) = x  
Vejamos se funciona:
ghci> head' [4,5,6]  
4  
ghci> head' "Olá"  
'O'  
Excelente! Saiba ainda que se você quiser especificar diversas variáveis (mesmo que uma delas seja _ e que isso não seja
realmente usado em algo), deveremos coloca­las entre parênteses. Perceba também a função error que usamos. Ela
recebe uma string e gera um runtime error, usando a string para nos informar o erro ocorrido. Já que ele causa a finalização
do programa, é bom usa­lo moderadamente. Mas chamar head com uma lista vazia não faz sentido, então...
Vamos então criar uma função muito útil que nos diz os primeiros elementos de uma lista de um modo mais
07/04/2016 Sintaxe em Funções ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/syntax­in­functions 4/11
(des)interessante.
tell :: (Show a) => [a] ‐> String  
tell [] = "A lista esta vazia"  
tell (x:[]) = "A lista tem apenas um elemento: " ++ show x  
tell (x:y:[]) = "A lista tem dois elementos: " ++ show x ++ " e " ++ show y  
tell (x:y:_) = "Esta lista esta longa demais. Veja os dois primeiros elementos: " ++ show x ++ " e " ++ 
Essa função é considerada segura porque prevê uma lista vazia, uma lista com apenas um elemento, uma com dois e outra
com mais elementos. O (x:[]) e (x:y:[]) poderiam ser escritos como [x] e [x,y] (como é algo mais simples, despensa­
se os parênteses). Ao contrário de (x:y:_), que são para listas de mais de dois elementos e nãopodem ser escritos com
colchetes.
Nós já implementamos a nossa própria length usando compreensão de listas. Agora tentaremos fazer usando pattern
matching e um pouco de recursão:
length' :: (Num b) => [a] ‐> b  
length' [] = 0  
length' (_:xs) = 1 + length' xs  
Isso é similar à função de fatorial que escrevemos anteriormente. Primeiro definimos o resultado do valor conhecido — a lista
vazia. Isso é conhecido também como edge condition. Então no segundo pattern dividimos a coitada da lista em primeiro
elemento e resto. Dissemos que a length da lista é igual a 1 somado com a length do "resto". Usamos _ para o primeiro
elemento porque não nos importamos com o seu valor. Perceba que nos preocupamos com os dois lados: uma lista vazia na
primeira linha e uma com mais de um elemento na segunda.
Então vamos descobrir o que acontece ao chamar length' com "ham". Primeiro, é testado se é uma lista vazia. Já que não
é, passa­se para o segundo pattern. Aí passamos no teste e o resultado da length é 1 + length' "am", porque dividimos a
lista em primeiro e resto e descartamos o primeiro elemento. Até aí tudo bem. length' de "am" é, semelhantemente,
1 + length' "m". Então agora temos 1 + (1 + length' "m"). length' "m" é 1 + length' "" (que também poderia ser
escrito como 1 + length' []). E já definimos que length' [] é 0. Logo, no fim temos 1 + (1 + (1 + 0)).
Vamos implementar sum. Sabemos que a soma de uma lista vazia deve ser 0. Escrevemos então como um pattern. E
também sabemos que a soma de uma lista é o primeiro elemento mais a soma do resto da lista. Se usarmos o mesmo
método, temos:
sum' :: (Num a) => [a] ‐> a  
sum' [] = 0  
sum' (x:xs) = x + sum' xs  
Ainda existe uma coisa chamada de as patterns. Eles são uma mão na roda quando precisamos usar um pattern e continuar
com ele acessível, mesmo depois de usado. Você cria isso colocando um @ na frente do nome do pattern. Por exemplo, o
pattern xs@(x:y:ys). Esse as patterns irá fazer exatamente a mesma coisa que x:y:ys, mas você consegue obter a lista
inteira usando apenas xs, ao invés repetir tudo novamente digitando x:y:ys no corpo da função novamente. Um exemplo
bem simples:
capital :: String ‐> String  
capital "" = "String vazia, oops!"  
07/04/2016 Sintaxe em Funções ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/syntax­in­functions 5/11
capital all@(x:xs) = "A primeira letra de " ++ all ++ " é " ++ [x]  
ghci> capital "Dracula"  
"A primeira letra de Dracula é D"  
Normalmente usamos esse recurso as patterns para evitar repetição de grandes patterns usadas durante a declaração de
uma função.
Mais uma coisa — você não pode usar ++ em patterns. Se você tentar usar o pattern (xs ++ ys), qual seria a primeira e
qual seria a segunda lista? Não faz muito sentido. Faria algum sentido usar (xs ++ [x,y,z]) ou (xs ++ [x]), mas pela
estrutura das listas isso não é possível.
Guardas, guardas!
Enquanto patterns é um jeito de ter certeza de que um valor tenha a forma desejada e torna possível
desmembra­lo, guards (guardas) são uma forma de testar se uma (ou mais) propriedades são verdadeiras
ou falsas. Se isso te parece demais com um if, está no caminho certo. A diferença é que guards são mais
fáceis de ler quando têm muitas condições e funcionam bem com patterns.
Ao invés de parar para explicar a sintaxe, vamos direto criar uma função usando guards. Faremos uma
função simples que repreende o usuário de uma forma específica, dependendo do seu IMC. Seu IMC é igual
à sua massa dividida pelo quadrado de sua altura. Se seu IMC está abaixo de 18,5, você é considerado
magro. Se está entre 18,5 e 25, é saudável. Entre 25 e 30, acima do peso. Maior, obeso. Então, esta é a função (ela não
calcula, somente mostra a mensagem)
bmiTell :: (RealFloat a) => a ‐> String  
bmiTell bmi  
    | bmi <= 18.5 = "Você esta abaixo do peso!"  
    | bmi <= 25.0 = "Supostamente você esta normal. Pfff, aposto que você é feio!"  
    | bmi <= 30.0 = "Você esta gordo! Faça uma dieta, gorducho!"  
    | otherwise   = "Você é uma baleia, meus parabéns!"  
Guards são marcados por pipes, seguidos do nome de uma função e seus parâmetros. Geralmente, são alinhados e
identados um pouco a direita. Um guarda geralmente é uma expressão booleana. Se é True, a função correspondente é
executada. Se False, o resto da linha não é executada e é passado para a próxima. Se chamarmos essa função com 24.3,
será testado se é menor ou igual a 18.5. Já que não é, o próximo guard é testado. A verificação tem sucesso ao passar pelo
segundo guard, já que 24,3 é menor que 25,0. Assim, é retornado o resultado do segundo guard.
Os guards são remanescentes de uma árvore encadeada de if/elses da linguagens imperativas, mas muito mais legíveis.
Apesar de árvores de if/elses encadeados serem extremamente desaconselhados, às vezes um problema é definido de um
modo que não há muitas alternativas. Em Haskell, Guards são uma solução.
Em vários casos, o último guard é otherwise. otherwise é definido com otherwise = True e aprova tudo. É muito parecido
com patterns, que testam por padrões ao invés de condições booleanas. Se todos os guards de uma função derem False (e
não tivermos especificado um guard otherwise), é testado o próximo pattern. É assim que patterns e guards funcionam bem
em conjunto. Se nenhum guard ou pattern se aplicar, é lançado um erro.
E é claro que podemos usar guards com funções que recebam quantos parâmetros desejarmos. Ao invés de obrigarmos o
07/04/2016 Sintaxe em Funções ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/syntax­in­functions 6/11
usuário a calcular o seu próprio IMC antes de usar a função, vamos modificá­la para receber altura e peso e descobrir
automaticamnte.
bmiTell :: (RealFloat a) => a ‐> a ‐> String  
bmiTell weight height  
    | weight / height ^ 2 <= 18.5 = "Você esta abaixo do peso!"  
    | weight / height ^ 2 <= 25.0 = "Supostamente você esta normal. Pfff, aposto que você é feio!"  
    | weight / height ^ 2 <= 30.0 = "Você esta gordo! Faça uma dieta, gorducho!"  
    | otherwise                 = "Você é uma baleia, meus parabéns!"  
Vamos descobrir se eu estou gordo...
ghci> bmiTell 85 1.90  
"Supostamente você esta normal. Pfff, aposto que você é feio!"  
Ei! Não estou gordo! Mas Haskell me chamou de feio mesmo assim. Que seja.
Veja que não há um = depois do nome da função e seus parâmetros, mas antes do primeiro guard. Muitos iniciantes recebem
erros de sintaxe por colocarem aí.
Mais uma outra bem simples: vamos implementar nosso próprio max. Se não se lembra, ele recebe dois parâmetros e retorna
o maior.
max' :: (Ord a) => a ‐> a ‐> a  
max' a b   
    | a > b     = a  
    | otherwise = b  
Guards também podem ser escritos em apenas uma linha, no entanto eu não recomendo graças a sua pouca legibilidade,
mesmo em funções curtas. Mas para fins de demonstração, poderíamos escrever nosso max' assim:
max' :: (Ord a) => a ‐> a ‐> a  
max' a b | a > b = a | otherwise = b  
Ugh! Nem um pouco legível! Próxima: nossa própria imprementação de compare usando guards.
myCompare :: (Ord a) => a ‐> a ‐> Ordering  
a `myCompare` b  
    | a > b     = GT  
    | a == b    = EQ  
    | otherwise = LT  
ghci> 3 `myCompare` 2  
GT  
Nota: Do mesmo modo que podemos chamar funções infixas usando crases em volta do seu nome, podemos defini­las.
Às vezes se torna mais fácil de se ler.
Onde!?
07/04/2016 Sintaxe em Funções ­ Aprender Haskell será um grande bem para você!
http://haskell.tailorfontela.com.br/syntax­in­functions 7/11
Na seção anterior, definimos uma função de calculadora de IMC que era algo parecido com isso:
bmiTell :: (RealFloat a) => a ‐> a ‐> String  
bmiTell weight height  
    | weight / height ^ 2 <= 18.5 = "Você esta abaixo do peso!"  
    | weight / height ^ 2 <= 25.0 = "Supostamente você esta

Outros materiais