├── .gitignore ├── cap03-tipos_typeclasses ├── cap03-part01.md ├── cap03-part02.md └── cap03-part03.md ├── cap13-pouco-mais-de-monads ├── cap13-part01.md ├── cap13-part05.md └── cap13-part03.md ├── cap14-zippers ├── cap14-part01.md ├── cap14-part02.md ├── cap14-part04.md ├── cap14-part06.md └── cap14-part05.md ├── cap04-sintaxe-funcoes ├── cap04-part05.md ├── cap04-part03.md ├── cap04-part04.md └── cap04-part02.md ├── cap01-introducao ├── cap01-part03.md ├── cap01-part01.md └── cap01-part02.md ├── README.md ├── cap11-functors-applicative-functors-monoids └── cap11-part01.md ├── cap05-recursao ├── cap05-part01.md ├── cap05-part05.md ├── cap05-part04.md ├── cap05-part02.md └── cap05-part03.md ├── cap06-funcoes-alta-ordem ├── cap06-part06.md ├── cap06-part04.md ├── cap06-part07.md ├── cap06-part02.md └── cap06-part01.yml ├── palavras-problematicas.txt ├── cap07-modulos ├── cap07-part05.md ├── cap07-part06.md ├── cap07-part01.md ├── cap07-part03.md └── cap07-part04.md ├── cap08-criando-tipos-typeclasses ├── cap08-part02.md ├── cap08-part08.md ├── cap08-part01.md ├── cap08-part04.md └── cap08-part03.md ├── cap02-comecando ├── cap02-part04.md ├── cap02-part05.md ├── cap02-part02.md ├── cap02-part06.md ├── cap02-part01.md └── cap02-part03.md └── cap12-punhado-de-monads ├── cap12-part03.md ├── cap12-part01.md ├── cap12-part02.md └── cap12-part07.md /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | -------------------------------------------------------------------------------- /cap03-tipos_typeclasses/cap03-part01.md: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/taylorrf/learnhaskell/HEAD/cap03-tipos_typeclasses/cap03-part01.md -------------------------------------------------------------------------------- /cap13-pouco-mais-de-monads/cap13-part01.md: -------------------------------------------------------------------------------- 1 | For a Few Monads More 2 | ===================== 3 | 4 | We've seen how monads can be used to take values with contexts and apply them to functions and how using [code]>>=[/code] or [code]do[/code] notation allows us to focus on the values themselves while the context gets handled for us. 5 | 6 | We've met the [code]Maybe[/code] monad and seen how it adds a context of possible failure to values. We've learned about the list monad and saw how it lets us easily introduce non-determinism into our programs. We've also learned how to work in the [code]IO[/code] monad, even before we knew what a monad was! 7 | 8 | In this chapter, we're going to learn about a few other monads. We'll see how they can make our programs clearer by letting us treat all sorts of values as monadic ones. Exploring a few monads more will also solidify our intuition for monads. 9 | 10 | The monads that we'll be exploring are all part of the [code]mtl[/code] package. A Haskell package is a collection of modules. The [code]mtl[/code] package comes with the Haskell Platform, so you probably already have it. To check if you do, type [code]ghc-pkg list[/code] in the command-line. This will show which Haskell packages you have installed and one of them should be [code]mtl[/code], followed by a version number. -------------------------------------------------------------------------------- /cap14-zippers/cap14-part01.md: -------------------------------------------------------------------------------- 1 | Zippers 2 | ======= 3 | 4 | Embora a pureza de Haskell seja acompanhada de vários benefícios, ela nos faz abordar alguns problemas diferentemente do que em linguagens impuras. Por causa da transparência referencial, um valor em Haskell é igual a outro se eles representem a mesma coisa. 5 | 6 | Então se tivermos uma árvore cheia de cincos e quisermos mudar um desses para seis, nós temos que saber exatamente qual cinco da nossa árvore nós queremos mudar. Temos que saber onde ele esta na nossa árvore. Em linguagens impuras, nós podemos só verificar na memória onde o cinco está localizado e muda-lo. Mas em Haskell, um cinco é igual ao outro, então não podemos diferencia-lo baseado na sua posição na memória. Nós também não podemos alterar qualquer valor; quando falamos em mudar uma árvore, isso significa que estamos recebendo uma árvore e devolvendo uma nova árvore semelhante a original, mas levemente diferente. 7 | 8 | Uma coisa que podemos fazer é lembrar o caminho da raiz da árvore até o elemento que queremos mudar. Nós podemos dizer, pegue a árvore, vá para esquerda, vá para direita e então esquerda novamente e mude o elemento que está nessa posição. Embora isso funcione, pode ser ineficiente. Se quisermos mais tarde mudar o elemento vizinho ao elemento previamente atualizado, temos que andar todo o caminho da raiz da árvore até o nosso elemento novamente! 9 | 10 | Nesse capítulo, nós iremos ver como podemos manipular uma estrutura de dados de forma fácil e eficiente. Legal! -------------------------------------------------------------------------------- /cap04-sintaxe-funcoes/cap04-part05.md: -------------------------------------------------------------------------------- 1 | Expressões case 2 | =============== 3 | 4 | Se você já programou em outras linguagens imperativas (C, C++, Java, etc.), sabe do que estou falando. É pegar uma variável e executar blocos de código específicos para cada valor pré-definido e possivelmente definir um bloco padrão no caso dela ter um valor inesperado. 5 | 6 | Haskell pega o conceito e o expande. Como o próprio nome sugere, expressões case são, bom, expressões, e muito semelhante com expressões if/else e associações let. Não só podemos testar expressões baseadas em seu valor, como podemos também usar pattern matching. Hmmmmm, pegar uma variável, procurar por um pattern, executar blocos de código para cada resultado... onde já vi isso? Ah claro, pattern matching em parâmetros de funções! Mas isso é só um atalho para executar uma expressão case. Esses dois códigos fazem a mesma coisa, então pode escolher qual você prefere: 7 | 8 | 9 | Como vê, a sintaxe de expressões case são extremamente simples: 10 | 11 | 12 | [code]expression[/code] é testada por alguns patterns. Pattern matching funciona exatamente como se presume: o primeiro pattern que verificar-se verdadeiro é executado. Se todo o case for executado e nenhum pattern se encaixar, acontece um runtime error. 13 | 14 | Enquanto pattern matching dentro dos parâmetros só podem ser feitos ao definir funções, expressões case podem ser colocadas praticamente em qualquer lugar. Olha só: 15 | 16 | 17 | Elas são úteis para usar pattern matching no meio de expressões. Já que pattern matching em definições de função são um atalho para expressões case, poderíamos ainda ter feito desse jeito: -------------------------------------------------------------------------------- /cap01-introducao/cap01-part03.md: -------------------------------------------------------------------------------- 1 | O que eu preciso para embarcar nessa 2 | ==================================== 3 | 4 | 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 o Haskell Platform, que é algo como o Haskell com esteróides. 5 | 6 | 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, [code] myfunctions.hs [/ code], você poderá carregar essas funções digitando [code]: l myfunctions [/ code] para então poder brincar com elas, desde que [code] myfunctions.hs [/ code] esteja na mesma pasta na qual o [code] ghci[/ code] foi invocado. Se você mudar o seu script ".hs", basta executar [code]: l myfunctions [/ code] novamente ou fazer [code] : r[/ code], 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. -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | Sobre 2 | ============== 3 | 4 | Projeto de tradução do livro "learn your a Haskell for great good" para o português do Brasil. 5 | 6 | Versão online em português: 7 | http://haskell.tailorfontela.com.br 8 | 9 | Versão online em inglês: 10 | http://www.learnyouahaskell.com 11 | 12 | 13 | ### Como contribuir 14 | 15 | Para revisões de tradução ou tradução de capitulos faça um fork deste repositório e submeta sua contribuição. 16 | 17 | No caso de traduções, verifique o status de cada capitulo abaixo, caso já esteja sendo traduzido escolha outro capitulo ou uma parte que ainda precisa de tradução. 18 | 19 | 20 | Revisões e correções ortográficas de capitulos já traduzidos são bem-vindas. 21 | 22 | 23 | ### Status da Tradução 24 | 25 | - [x] capitulo01 26 | - [x] capitulo02 27 | - [x] capitulo03 28 | - [x] capitulo04 29 | - [x] capitulo05 30 | - [x] capitulo06 31 | - [x] capitulo07 32 | - [x] capitulo08 33 | - [x] capitulo09 34 | - [x] capitulo10 35 | - [x] capitulo11-part01 36 | - [ ] capitulo11-part02 (em tradução por @taylorrf) 37 | - [ ] capitulo11-part03 (em tradução por @phinfonet) 38 | - [x] capitulo11-part04 39 | - [ ] capitulo11-part05 40 | - [x] capitulo12-part01 41 | - [x] capitulo12-part02 42 | - [x] capitulo12-part03 43 | - [x] capitulo12-part04 44 | - [ ] capitulo12-part05 45 | - [ ] capitulo12-part06 46 | - [ ] capitulo12-part07 47 | - [ ] capitulo13-part01 48 | - [ ] capitulo13-part02 49 | - [ ] capitulo13-part03 50 | - [ ] capitulo13-part04 51 | - [ ] capitulo13-part05 52 | - [ ] capitulo13-part06 53 | - [ ] capitulo13-part07 54 | - [x] capitulo14-part01 55 | - [x] capitulo14-part02 56 | - [x] capitulo14-part03 57 | - [x] capitulo14-part04 58 | - [ ] capitulo14-part05 59 | - [ ] capitulo14-part06 60 | -------------------------------------------------------------------------------- /cap03-tipos_typeclasses/cap03-part02.md: -------------------------------------------------------------------------------- 1 | Tipo variável 2 | ============= 3 | 4 | Qual você acha que é o tipo da função [code]head[/code]? Já que [code]head[/code] recebe uma lista e 5 | retorna o seu primeiro elemento, qual deve ser o seu tipo? Vamos descobrir! 6 | 7 | Hmmm! O que é esse [code]a[/code]? É o tipo? Lembre-se que já vimos que o tipo é escrito com a primeira 8 | letra maiúscula, então não pode ser exatamente o tipo. E é exatamente essa diferença que nos diz ser 9 | um tipo variável. Isso significa que o [code]a[/code] pode ser qualquer tipo. Isso é algo como 10 | os genéricos de outras linguagens, mas em Haskell é muito mais poderoso porque nos permite facilmente 11 | escrever funções mais genéricas caso o processamento seja o mesmo para diferentes tipos. Funções que 12 | possuem tipos variáveis são denominadas funções polimórficas. A declaração de tipo em 13 | [code]head[/code] diz que ele recebe uma lista de elementos de qualquer tipo e retorna um elemento dela. 14 | 15 | Embora tipos variáveis possam ter nomes com mais de um caractere, normalmente nós damos a eles nomes 16 | como a, b, c, d... 17 | 18 | Se lembra da função [code]fst[/code]? Aquela que retorna o primeiro componente de um par? Então, vamos 19 | examinar o seu tipo: 20 | 21 | Examinando o tipo de [code]fst[/code], a gente vê que ele recebe uma tupla que contém dois tipos e 22 | retorna o primeiro elemento. É exatamente por causa disso que conseguimos usar [code]fst[/code] em 23 | pares que contenham quaisquer tipos. Perceba ainda que mesmo [code]a[/code] e [code]b[/code] sendo 24 | diferentes tipos variáveis, eles não devem ser necessariamente de tipos diferentes. A declaração 25 | apenas nos diz que o tipo do primeiro componente (da tupla) deve ser do mesmo que o do retorno. 26 | -------------------------------------------------------------------------------- /cap11-functors-applicative-functors-monoids/cap11-part01.md: -------------------------------------------------------------------------------- 1 | Functors, Applicative Functors e Monoids 2 | ======================================== 3 | 4 | A combinação de pureza em Haskell, funções de ordem superior, tipos de dados algébricos parametrizados e 5 | typeclasses nos permite implementar polimorfismo em um nível muito mais alto em relação as outras 6 | linguagens. Não precisamos pensar sobre a dependência dos tipos em uma grande hierarquia de tipos. 7 | Ao invés disso, pensamos a respeito de como os tipos podem se comportar e se conectar com as typeclasses 8 | apropriadas. Um [code]Int[/code] pode se comportar como um monte de coisas. Ele pode se comportar como 9 | algo que compara a igualdade de coisas, como algo que ordena, como algo que enumera as coisas, etc. 10 | 11 | As typeclasses são abertas, o que nos permite definir os nossos próprios tipos de dados, pense sobre como 12 | algo deve se comportar e conecte isso com as typeclasses que definem esse comportamento. Por causa disso 13 | e por causa do belo sistema de tipos de Haskell, que nos permite saber bastante sobre uma função apenas 14 | olhando para a sua declaração de tipo, podemos definir typeclasses que definem um comportamento bem 15 | amplo e abstrato. Nós já fomos apresentados a typeclasses que definem as operações que inspecionam 16 | duas coisas quaisquer e nos dizem se elas são iguais ou que comparam a ordem delas. Esses são 17 | comportamentos bastante abstratos e elegantes, mas nós já não pensamos neles como algo super ultra especial porque geralmente lidamos com eles ao longo de boa parte das nossas vidas. Recentemente nós descobrimos os functors, que basicamente são coisas que podem ser mapeadas. Isso é um exemplo de 18 | uma propriedade bastante útil e abstrata que as typeclasses podem descrever. Neste capítulo vamos 19 | dar uma olhada bem de perto em functors, juntamente com uma versão mais forte e útil de functors chamada de applicative functors. Vamos também dar uma boa olhada em monoids, que são uma espécie de isolante. -------------------------------------------------------------------------------- /cap05-recursao/cap05-part01.md: -------------------------------------------------------------------------------- 1 | Recursão 2 | ======== 3 | 4 | ** Olá recursão! ** 5 | 6 | Nós já mencionamos recursão brevemente no capítulo anterior. Nesse capítulo, veremos recursão 7 | mais de perto, porque ela é importante em Haskell por nos permitir construir soluções muito concisas e 8 | elegantes para problemas pensando recursivamente. 9 | 10 | Se você ainda não sabe o que é recursão, leia essa frase do ínicio novamente. Hahaha! Estou só brincando! 11 | Recursão é na verdade uma forma de definir funções onde ela própria é usada em sua definição. Geralmente 12 | definições em matemática são dadas recursivamente. Por exemplo, a sequência Fibonacci é definida 13 | recursivamente. Primeiro, definimos explicitamente os primeiros dois números de Fibonacci. 14 | Dizemos que F(0) = 0 e F(1) = 1, o que significa que o 0-ésimo e primeiro números de 15 | Fibonacci são 0 e 1, respectivamente. Então dizemos que, para qualquer outro número natural, aquele 16 | número de Fibonacci será a soma dos dois números de Fibonacci anteriores. Então F(n) = F(n-1) + F(n-2). 17 | 18 | Dessa forma, F(3) é F(2) + F(1), que é (F(1) + F(0)) + F(1). Como nós agora chegamos 19 | a somente números de Fibonacci definidos não-recursivamente, nós podemos seguramente dizer que 20 | F(3) é 2. Um ou dois elementos definidos não-recursivamente numa definição recursiva 21 | (como F(0) e F(1) aqui) são chamados também de condições limites e são 22 | importantes caso você queira que sua função recursiva termine em algum momento. Se não tivéssemos 23 | definido F(0) e F(1) não-recursivamente, você nunca encontraria número algum na solução, 24 | já que chegaria ao 0 e então continuaria nos números negativos. De repente, você estaria dizendo que 25 | F(-2000) é F(-2001) + F(-2002) e ainda assim não haveria um fim à vista! 26 | 27 | Recursão é importante em Haskell porque, diferentemente das linguagens imperativas, você faz 28 | computações em Haskell declarando o que alguma coisa é ao invés de declarar como você 29 | chegou a ela. Por essa razão não há laços while ou for em Haskell. Por isso muitas vezes 30 | temos que usar recursão para declarar o que alguma coisa é. 31 | -------------------------------------------------------------------------------- /cap01-introducao/cap01-part01.md: -------------------------------------------------------------------------------- 1 | Introdução 2 | ========== 3 | 4 | ** Sobre este tutorial ** 5 | 6 | Bem-vindo ao Aprender Haskell será um grande bem para você! 7 | 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. 8 | 9 | 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. 10 | 11 | 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. 12 | 13 | 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. 14 | 15 | Eu falhei aproximadamente 2 vezes antes de finalmente aprender Haskell, isto porque tudo me parecia muito estranho e eu não conseguia entender. 16 | 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 … 17 | -------------------------------------------------------------------------------- /cap05-recursao/cap05-part05.md: -------------------------------------------------------------------------------- 1 | Pensando recursivamente 2 | ======================= 3 | 4 | Já fizemos alguma coisa de recursão até agora e, como você provavelmente já notou, existe um padrão 5 | aqui. Normalmente você define uma condição limite e então você define uma função que faz alguma coisa 6 | com o primeiro elemento e reaplica no resto. Não importa se é uma lista, uma árvore ou qualquer outra 7 | estrutura de dados. Uma soma é o primeiro elemento de uma lista mais a soma do resto da lista. O produto 8 | de uma lista é o primeiro elemento da lista multiplicado pelo produto do resto da lista. O tamanho de 9 | uma lista é primeiro elemento mais o tamanho do resto da lista. E por ai vai. 10 | 11 | E claro, falamos também das condições limite. Normalmente o caso limite é um cenário onde uma aplicação 12 | recursiva não faz sentido. Quando se trata de listas, a condição limite é na maioria das vezes uma 13 | lista vazia. Se você está lidando com árvores, o caso limite é normalmente um nodo que não tem filho algum. 14 | Coitado... 15 | 16 | Não é diferente quando estamos lidando com números recursivamente. Um número faz algum cálculo com 17 | alguma modificação dele. Quando fizemos a função de fatorial, era o número multiplicado pelo fatorial (dele - 1). 18 | Nesse caso, tal aplicação recursiva não fazia sentido com zero, já que fatoriais estão definidos somente 19 | para inteiros positivos. Frequentemente os valores das condições limite acabam sendo sua identidade. 20 | A identidade da multiplicação é 1 porque se você multiplicar algum número por 1, você recebe de volta 21 | ele mesmo. Também ao fazer somas de listas, definimos a soma de uma lista vazia como 0 e 0 é a identidade 22 | da adição. No quicksort, o caso limite é a lista vazia e a identidade é também a lista vazia, porque se 23 | você adicionar uma lista vazia a uma lista, você apenas recebe de volta a lista original. 24 | 25 | Então quando você tentar pensar de forma recursiva para resolver um problema, tente pensar em quando 26 | a solução recursiva não se aplica e tente ver se você pode usar isso como uma condição limite. Pense sobre 27 | identidades, se você vai dividir em partes os parâmetros da função (por exemplo, listas são normalmente 28 | divididas em primeiro elemento e resto, através de pattern matching) e em que parte você usará 29 | chamadas recursivas. 30 | -------------------------------------------------------------------------------- /cap06-funcoes-alta-ordem/cap06-part06.md: -------------------------------------------------------------------------------- 1 | Aplicação de Função com $ 2 | ========================= 3 | 4 | Beleza, agora vamos dar uma olhada na função [code]$[/code], também chamada de 5 | aplicação de função. Antes de qualquer coisa, vamos ver como isto é definido: 6 | 7 | Que negócio é esse? Qual a utilidade desse operador? Ele nada mais é do que uma aplicação de função! 8 | Bem, quase foi agora, mas não muito! Enquanto uma aplicação de função normal (que coloca um espaço 9 | entre duas coisas) tem realmente uma alta precedência, a função [code]$[/code] tem baixa precedência. 10 | Aplicar função com um espaço associa à esquerda (ou seja, [code]f a b c[/code] é o mesmo que 11 | [code]((f a) b) c)[/code])), aplicar função com [code]$[/code] associa à direita. 12 | 13 | Isto tudo é muito bacana, mas como isso irá me ajudar? Na maioria dos casos, esta é uma função 14 | conveniente, ou seja, nós não precisamos escrever muitos parênteses. Considere a expressão 15 | [code]sum (map sqrt [1..130])[/code]. Como [code]$[/code] tem baixa precedência nós podemos reescrever 16 | esta expressão como [code]sum $ map sqrt [1..130][/code], salvando nós mesmo de preciosas teclas não 17 | digitadas! Quando um [code]$[/code] é encontrado, a expressão a sua direita é aplicada como um parâmetro 18 | da função a sua esquerda. Como seria [code]sqrt 3 + 4 + 9[/code]? Isto soma juntos 9, 4 e a raiz quadrada 19 | de 3. Se quisermos a raiz quadrada de 3 + 4 + 9, temos que escrever [code]sqrt (3 + 4 + 9)[/code] 20 | ou se usarmos [code]$[/code] podemos escrever como [code]sqrt $ 3 + 4 + 9[/code] porque [code]$[/code] 21 | tem a menor precedência sobre qualquer operador. Este é o porque que você deve imaginar um 22 | [code]$[/code] como sendo uma espécie do equivalente a escrever um parênteses para abrir e outro para 23 | fechar na direita da sua expressão. 24 | 25 | E como seria [code]sum (filter (> 10) (map (*2) [2..10]))[/code]? Bem, como [code]$[/code] associa à 26 | direita, [code]f (g (z x))[/code] é igual a [code]f $ g $ z x[/code]. E então nós pode reescrever 27 | [code]sum (filter (> 10) (map (*2) [2..10]))[/code] como 28 | [code]sum $ filter (> 10) $ map (*2) [2..10][/code]. 29 | 30 | Mas além de nos livrar de parênteses, [code]$[/code] significa que a aplicação de função pode ser 31 | tratada como apenas outra função. Dessa forma, nós podemos, por exemplo, mapear a aplicação de função 32 | em uma lista de funções. -------------------------------------------------------------------------------- /palavras-problematicas.txt: -------------------------------------------------------------------------------- 1 | palavras-problematicas.txt 2 | 3 | O arquivo tem por objetivo listar as palavras com tradução "difícil" e começar a 4 | discussão sobre como seria conveniente que elas fossem traduzidas, levando em 5 | consideração o nível de conhecimento do leitor habitual do livro e o quanto a 6 | não-tradução dessas palavras já está difundida no meio desse leitor. 7 | 8 | -------------------------------------------------------------------------------- 9 | Palavras não-traduzidas (que são "reservadas" da linguagem ou que guardam algum 10 | valor muito próximo da sintaxe da linguagem) 11 | 12 | typeclass classe de tipos 13 | subclass subclasse, subclassess (plural) 14 | otherwise 15 | "where" binding ligação "where" 16 | functor functor, functores (plural) 17 | applicative functors 18 | monoids monoide 19 | 20 | -------------------------------------------------------------------------------- 21 | Palavras "complicadas" de traduzir 22 | 23 | edge condition condição limite 24 | maximum máximo 25 | max maior 26 | singleton lista de um só elemento 27 | fibonacci number número de fibonacci 28 | head (da lista) cabeça 29 | tail (da lista) cauda 30 | pattern matching reconhecimento/casamento de padrões 31 | evaluate avaliar em 32 | to zip mesclar / chamar "zip" em 33 | type signature assinatura de tipo 34 | list comprehension compreensão de lista (conforme wikipedia) 35 | stream stream 36 | pipe/piping pipe/fazendo piping 37 | handle handle 38 | curry curriar 39 | type synonym tipo sinônimo 40 | flush descarregar 41 | fixity precedência 42 | -------------------------------------------------------------------------------- /cap04-sintaxe-funcoes/cap04-part03.md: -------------------------------------------------------------------------------- 1 | Onde!? 2 | ====== 3 | 4 | Na seção anterior, definimos uma função de calculadora de IMC que era algo parecido com isso: 5 | 6 | 7 | Note ainda que nos repetimos três vezes. Nós nos repetimos três vezes. Repetir-se (três vezes) no código é tão desejável quando levar um chute bem no meio da testa. Já que repetimos a expressão três vezes, seria melhor se calculássemos apenas uma vez, gravássemos numa variável para usarmos nos próximos comandos ao invés da expressão. Para isso, modificamos nossa função para isso: 8 | 9 | 10 | Colocamos a palavra-chave [code]where[/code] (geralmente identamos até onde estão os pipes) e definimos variáveis ou funções. Esses nomes são visíveis dentro dos guards e nos permitem não ficar nos repetindo. Se decidirmos que iremos calcular o IMC de um modo diferente, precisamos mudar apenas uma vez. Podemos avançar um pouco e deixar nossa função assim: 11 | 12 | 13 | Os nomes criados na seção where só são visíveis dentro da função, então não temos de nos preocupar com elas poluindo o namespace de outras funções. Note também que todos os nomes foram alinhados na mesma coluna. Se não o fizéssemos, Haskell ficaria confuso e não saberia que eles fazem parte do mesmo bloco. 14 | 15 | Associações where não são compartilhadas entre diferentes patterns. Se você quiser que vários patterns de uma mesma função compartilhem um determinado nome, você deverá especificá-los como global. 16 | 17 | Você também pode usar where em conjunto com pattern match! Poderíamos reescrever a parte where da nossa função anterior como: 18 | 19 | 20 | Vamos fazer agora outra função extremamente necessária que recebe nome e sobrenome e retorna suas iniciais. 21 | 22 | 23 | Poderíamos fazer esse pattern matching diretamente nos parâmetros da função (o que resultaria em um código mais limpo) mas é só para mostrar que é possível também fazer isso usando o where. 24 | 25 | Assim como definimos constantes em blocos where, você também pode definir funções. Voltando ao nosso tema de programação saúdavel, vamos fazer uma função que pega uma lista de pesos/altura e retorna sua lista de IMC. 26 | 27 | 28 | E por hoje é só! A razão pela qual demos o [code]IMC[/code] como exemplo de função é que não poderíamos calcular diretamente nos seus parâmetros. Se vermos a lista passada pela função, veremos que cada par possui um IMC diferenciado. 29 | 30 | Associações where também podem ser aninhadas. É comum criar uma função e definir algum helper com suas cláusulas com funções e daí criar funções helper com suas próprias cláusulas where. 31 | -------------------------------------------------------------------------------- /cap07-modulos/cap07-part05.md: -------------------------------------------------------------------------------- 1 | Data.Set 2 | ======== 3 | 4 | O módulo [code]Data.Set[/code] nos oferece conjuntos. Conjuntos matemáticos, isso mesmo. 5 | Conjuntos são algo entre listas e mapas. Todos os elementos em um conjunto são únicos. E por serem 6 | internamente implementados por árvores (semelhantemente a mapas do [code]Data.Map[/code]), são 7 | ordenados. Checar existência, inserção, deleção, etc. é muito mais rápido do que com listas. As 8 | operações mais comuns falando-se de conjuntos é inserir, checar existência e converter para lista. 9 | 10 | Pelos nomes do [code]Data.Set[/code] frequentemente conflitarem com membros de [code]Prelude[/code] e 11 | [code]Data.List[/code], fazemos uma importação qualificada. 12 | 13 | Coloque essa linha no seu script: 14 | 15 | 16 | E então carregue-o via GHCI. 17 | 18 | Temos dois pedaços de um texto. Queremos descobrir quais caracteres são usados em ambos. 19 | 20 | 21 | A função [function]fromList[/function] faz exatamente o que você imagina. Recebe uma lista e 22 | converte-a em um conjunto. 23 | 24 | 25 | 26 | Como pode ver, os itens são ordenados e cada elemento são únicos. Usaremos 27 | [function]intersection[/function] para descobrir quais estão em ambos. 28 | 29 | 30 | 31 | Podemos ainda usar [function]difference[/function] para ver quais letras estão no primeiro mas não 32 | no segundo conjunto e vice-versa. 33 | 34 | 35 | Ou também podemos gerar um terceiro conjunto com as letras que aparecem em qualquer um dos dois 36 | primeiros usando [function]union[/function]. 37 | 38 | 39 | 40 | As funções [function]null[/function], [function]size[/function], [function]member[/function], 41 | [function]empty[/function], [function]singleton[/function], [function]insert[/function] e 42 | [function]delete[/function] você já deve imaginar para que servem. 43 | 44 | 45 | Nós ainda podemos procurar por subconjuntos ou superconjuntos. O conjunto A é um subconjunto de 46 | B se B contém todos os elementos que A também tem. O conjunto A é um superconjunto de B se B contém 47 | todos os elementos de A e mais alguns. 48 | 49 | 50 | Podemos ainda usar a [function]map[/function] e [function]filter[/function] para filtrá-los. 51 | 52 | 53 | 54 | Conjuntos geralmente são usados para eliminar de uma lista valores duplicados transformando em 55 | [code]fromList[/code] e convertendo de volta para uma lista com [function]toList[/function]. 56 | A [code]Data.List[/code] [code]nub[/code] já faz isso, mas remover duplicados com listas grandes 57 | é muito mais rápido convertendo primeiro para um conjunto e depois convertendo de volta. Mas usar 58 | [code]nub[/code] requer que os tipos dos elementos da lista estejam na typeclass [code]Eq[/code], 59 | enquanto para converter a lista em um elemento, deve estar em [code]Ord[/code]. 60 | 61 | 62 | 63 | [code]setNub[/code] geralmente é mais rápido do que [code]nub[/code] em listas grandes, mas como pode 64 | ver, [code]nub[/code] preserva a ordem dos elementos, enquanto [code]setNub[/code] não. 65 | -------------------------------------------------------------------------------- /cap08-criando-tipos-typeclasses/cap08-part02.md: -------------------------------------------------------------------------------- 1 | Sintaxe de registro 2 | =================== 3 | 4 | Certo, recebemos a tarefa de criar um tipo de dado que descreva uma pessoa. A informação que desejamos armazenar sobre a pessoa é: nome, sobrenome, idade, altura, número do telefone e sabor favorito de sorvete. Não sei você, mas isso é tudo que eu quero saber sobre uma pessoa. Vamos testar! 5 | 6 | 7 | Certo. O primeiro campo é o nome, o segundo é o sobrenome, o terceiro é a idade e assim por diante. Vamos fazer uma pessoa. 8 | 9 | 10 | Até que é legal, entretanto levemente ilegível. E se nós quisermos criar uma função que consiga informação separada da pessoa? Uma função que nos dá o nome da pessoa, uma função que nos dá o sobrenome da pessoa, etc. Bem, teríamos que defini-las mais ou menos dessa forma. 11 | 12 | 13 | Vish! Eu certamente não curti escrever tudo isso! Apesar disso ser muito incômodo e ENTEDIANTE de se escrever, esse método funciona. 14 | 15 | 16 | Deve existir um jeito melhor, você deve estar pensando! Bem, não, não há, desculpe. 17 | 18 | Brincadeira, tem sim. Hahaha! Os criadores de Haskell eram muito espertos e anteciparam esse cenário. Eles incluíram uma forma alternativa de se escrever tipos de dados. Aqui está como nós poderíamos alcançar a funcionalidade mostrada acima utilizando sintaxe de registro. 19 | 20 | 21 | Então ao invés de apenas nomear os tipos de campos um após o outro e separá-los com espaços, nós utilizamos chaves. Primeiro escrevemos o nome do campo, por exemplo, [code]firstName[/code], depois escrevemos dois pontos duplos [code]::[/code] (também chamados de Paamayim Nekudotayim, haha) e então especificamos seu tipo. O tipo de dado resultante é exatamente o mesmo. O principal benefício disso é que se cria funções que consultam os campos no tipo de dado. Ao utilizar sintaxe de registro para criar esse tipo de dado, Haskell automaticamente criou essas funções: 22 | [code]firstName[/code], [code]lastName[/code], [code]age[/code], [code]height[/code], [code]phoneNumber[/code] and [code]flavor[/code]. 23 | 24 | 25 | 26 | Ainda há outro benefício ao se utilizar sintaxe de registro. Quando derivamos [code]Show[/code] para o tipo, ele se exibe diferentemente se utilizarmos sintaxe de registro para definir e instanciar o tipo. Digamos que nós temos um tipo que representa um carro. Queremos manter o registro da companhia que o fez, o nome do modelo e o ano de sua produção. 27 | Observe. 28 | 29 | 30 | Se nós o definirmos utilizando sintaxe de registro, podemos fazer um novo carro dessa maneira. 31 | 32 | 33 | Quando estamos fazendo um novo carro, não temos que necessariamente colocar os campos na ordem exata, contanto que listemos todos eles. Mas se não utilizarmos sintaxe de registro, temos de especificá-los em ordem. 34 | 35 | Utilize sintaxe de registro quando um construtor tiver vários campos e não for óbvio o que cada campo tem. Se fizermos um tipo de dado de um vetor 3D escrevendo [code]data Vector = Vector Int Int Int[/code], é bem óbvio que os campos são componentes de um vetor. Entretanto, nos tipo de [code]Person[/code] e [code]Car[/code], não era tão óbvio e nós nos beneficiamos muito do uso de sintaxe de registro. 36 | -------------------------------------------------------------------------------- /cap02-comecando/cap02-part04.md: -------------------------------------------------------------------------------- 1 | Texas ranges 2 | ============ 3 | 4 | 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 caractéres de A a Z. Nomes também podem ser enumerados. O que vem depois de "João"? Não faço idéia. 5 | 6 | Para constituir uma lista que contenha todos os números naturais de 1 a 20, você simplesmente deve escrever [code][1..20][/code]. Isto é o equivalente a ter escrito [code][1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20][/code] 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. 7 | 8 | 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? 9 | 10 | 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 [code][1,2,4,8,16..100][/code] 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. 11 | 12 | Para ter uma lista com todos os números entre 20 e 1, você não pode simplesmente fazer [code][20..1][/code], você deve fazer [code][20,19..1][/code]. 13 | 14 | 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. 15 | 16 | Meu conselho é que não se faça uso deles em ranges de listas. 17 | 18 | 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 [code][13,26..24*13][/code]. Porém eis aqui um jeito melhor: [code]take 24 [13,26..][/code]. 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. 19 | 20 | Algumas funções para produzirmos listas infinitas: 21 | 22 | [code]cycle[/code] 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. 23 | 24 | [code]repeat[/code] recebe um elemento e produz uma lista infinita dele. Isto é como o ciclo de uma lista com somente um elemento. 25 | 26 | No entanto será mais simples utilizar a função [code]replicate[/code] caso você deseje algumas repetições do mesmo elemento em uma lista. [code]replicate 3 10[/code] retornará [code][10,10,10][/code]. -------------------------------------------------------------------------------- /cap05-recursao/cap05-part04.md: -------------------------------------------------------------------------------- 1 | Ordem, rápido! 2 | ============== 3 | 4 | Vamos supor que temos uma lista de itens que podem ser ordenados. Seu tipo é uma instância da 5 | typeclass [code]Ord[/code] e agora queremos ordená-los! Existe um algoritmo muito legal para ordenação 6 | chamado quicksort. Que é uma maneira 7 | muito inteligente de ordenar itens. Para implementar ele em uma linguagem imperativa geralmente precisamos 8 | de 10 ou mais linhas, enquanto que em Haskell além de mais curta, será mais elegante. 9 | O quicksort se tornou um tipo de garoto propaganda do Haskell. Portanto, vamos implementá-lo, mesmo 10 | sabendo que fazer o quicksort em Haskell é considerado jogo sujo. 11 | 12 | Então, a nossa declaração de tipo será [code]quicksort :: (Ord a) => [a] -> [a][/code]. 13 | Nada de novo. A condição limite? Lista vazia, como já esperado. Uma lista vazia ordenada é uma lista 14 | vazia. Agora vem o algoritmo principal: uma lista ordenada é uma lista composta de uma outra lista 15 | ordenada até um elemento X de valores menores que X, seguido de outra lista ordenada de valores maiores 16 | que X. Note que falamos de duas listas ordenadas, então provavelmente teremos que fazer 17 | uma chamada recursiva duas vezes! Note ainda que usamos o termo é para definir o algoritmo em vez 18 | de dizer faz isto, faz aquilo, então faz aquele outro.... Essa é a beleza da programação funcional! 19 | Mas como vamos filtrar a lista para somente obter elementos menores que a cabeça da nossa lista e 20 | somente elementos maiores que ela? Compreensão de listas. Vamos então definir logo de uma vez essa função. 21 | 22 | 23 | Vamos fazer uma pequena rodada de testes para ver se ela está se comportando corretamente. 24 | 25 | 26 | É isso ai! Era disso que eu tava falando! Então se a gente tiver algo como [code][5,1,9,4,6,7,3][/code] e 27 | quiser ordenar isso, o algoritmo primeiro pegará o primeiro elemento que é [code]5[/code] e colocará ele entre 28 | as listas de números menores e de números maiores. Assim, você passará a ter [code][1,4,3] ++ [5] ++ [9,6,7][/code]. 29 | Nós sabemos que uma vez a lista esteja completamente ordenada, o número [code]5[/code] ficará na quarta posição, 30 | já que há 3 números menores que ele e 3 números maiores que ele. Agora basta ordenarmos [code][1,4,3][/code] e 31 | [code][9,6,7][/code] para termos uma lista ordenada! Ordenamos as duas listas usando a mesma função. 32 | Eventualmente, quebraremos a lista tantas vezes que chegaremos a uma lista vazia que, pela nossa 33 | definição, já é uma lista ordenada. Aí vai uma ilustração: 34 | 35 | 36 | Um elemento que já encontrou seu lugar final é representado pela cor [orange]laranja[/code]. Se você 37 | lê-los da esquerda para a direita, verá a lista ordenada. Apesar de nós termos escolhido comparar todos 38 | os elementos com o primeiro, poderíamos ter usado qualquer outro elemento. No quicksort, o elemento 39 | que você irá comparar é chamado de pivô. Ele aqui esta em [green]verde[/code]. 40 | Nós escolhemos o primeiro porque é mais fácil de pegar usando pattern matching. Os elementos que são 41 | menores que o pivô estão em [green_light]verde claro[/code] e os elementos maiores que o pivô estão em 42 | [green_bold]verde escuro[/code]. E aquele negócio amarelo no meio de tudo representa a execução do quicksort. 43 | -------------------------------------------------------------------------------- /cap06-funcoes-alta-ordem/cap06-part04.md: -------------------------------------------------------------------------------- 1 | Lambdas 2 | ======= 3 | 4 | Lambdas são basicamente funções anônimas que utilizamos quando precisamos de alguma função 5 | somente uma vez. Normalmente, nós criamos um lambda com o propósito único de passá-lo para uma função 6 | de ordem superior. Para criar um lambda, digitamos um [code]\[/code] (porque isso se parece com a letra 7 | grega lambda caso você seja vesgo o suficiente) e então nós escrevemos os parâmetros separados por 8 | espaços. Depois vem o [code]->[/code] e então o corpo da função. Normalmente cercamos com parênteses, 9 | porque senão isso se estende para todo o lado direito. 10 | 11 | Se você olhar 902px acima, você verá que nós utilizamos uma associação where em nossa função 12 | [code]numLongChains[/code] para fazer a função [code]isLong[/code] com o único propósito de passar 13 | isso para o [code]filter[/code]. Bem, ao invés de fazermos isso, podemos utilizar um lambda: 14 | 15 | Lambdas são expressões, este é o porque de só passarmos aquilo. A expressão 16 | [code](\xs -> length xs > 15)[/code] retorna uma função que nos diz se o length da lista que foi 17 | passada é maior do que 15. 18 | 19 | Pessoas que não se familiarizaram muito bem antes com o funcionamento de currying e aplicações parciais 20 | utilizam lambdas onde não deveriam. Por exemplo, as expressões [code]map (+3) [1,6,3,2][/code] e 21 | [code]map (\x -> x + 3) [1,6,3,2][/code] são equivalentes desde que ambos [code](+3)[/code] e 22 | [code](\x -> x + 3)[/code] sejam funções que peguem um número e adicione 3 nele. Não preciso dizer 23 | que criar um lambda neste caso seria uma estupidez já que com a utilização da aplicação parcial fica 24 | bem mais legível. 25 | 26 | Como uma função normal, lambdas podem receber quantos parâmetros você quiser: 27 | 28 | E como uma função normal, você pode utilizar um pattern match em lambdas. A única diferença é que você 29 | não define alguns patterns para um único parâmetro, como fazer um [code][][/code] e um pattern 30 | [code](x:xs)[/code] para o mesmo parâmetro e depois ter valores disso. Se um pattern matching falhar 31 | em um lambda, um erro de runtime ocorrerá, portanto seja cuidadoso com pattern matching em lambdas! 32 | 33 | Lambdas são normalmente cercadas por parênteses a não ser que você queira estende-la por todo o lado 34 | direito. Uma coisa interessante: como funções são curried por default, estes dois são equivalentes: 35 | 36 | Se definirmos uma função como essa, é óbvio que a declaração de tipo é o que é. Lá estão os 37 | [code]->[/code] em ambas declaração de tipo e a equação. Mas é claro, o primeiro jeito de escrever 38 | a função é bem mais legível enquanto o segundo é muito mais uma demonstração do modo currying. 39 | 40 | Tanto faz, é legal quando utilizamos estas notações. Eu acho que aquela função [code]flip[/code] 41 | fica bem mais legível quando definimos assim: 42 | 43 | Apesar de isso ser o mesmo que escrever [code]flip' f x y = f y x[/code], fazemos assim obviamente 44 | por ser o mais utilizado na criação de uma nova função na maioria dos casos. O caso mais comum na 45 | utilização do [code]flip[/code] é somente chamá-lo com o parâmetro da função e então passar o 46 | resultado para um mapa ou um filtro. Utilize lambdas desse jeito quando você quiser deixar explicito 47 | que a sua função é destinada a ser parcialmente aplicada e passada em uma função como um parâmetro. 48 | -------------------------------------------------------------------------------- /cap05-recursao/cap05-part02.md: -------------------------------------------------------------------------------- 1 | Maior do melhor 2 | =============== 3 | 4 | A função [code]maximum[/code] recebe uma lista de coisas que podem ser ordenadas (por exemplo, 5 | instâncias da typeclass [code]Ord[/code]) e retorna a maior delas. Pense em como você implementaria 6 | isso de forma imperativa. Provavelmente você criaria uma variável para guardar o valor máximo encontrado 7 | até então e aí percorreria cada um dos elementos da lista. Caso um elemento fosse maior que o máximo, 8 | você o substituiria pelo anterior. O máximo que permanecesse ao fim seria o resultado. Ei! Foram bastante 9 | palavras para definir um algoritmo tão simples! 10 | 11 | Agora vamos pensar em como definiríamos isso recursivamente. Poderíamos já dizer que o maior número de uma lista 12 | com um elemento é ele próprio. Então que o maior de uma lista com mais de um elemento é o primeiro 13 | elemento, caso este seja maior que o maior do resto. Se for o maior do resto, bem, então será ele. 14 | Isso! Agora vamos implementar em Haskell. 15 | 16 | Como você pode ver, pattern matching e recursão formam uma grande dupla! A maioria das linguagens 17 | imperativas não tem pattern matching, o que leva você a criar muitas estruturas if/else para testar 18 | as condições limites. Aqui, nós simplesmente as colocamos como patterns. Então a primeira condição 19 | limite diz que se uma lista está vazia, crash! Faz sentido porque qual é o maior elemento de um lista 20 | vazia? Eu não sei. O segundo pattern também funciona como uma condição limite. Ele diz que se a lista 21 | tem apenas um único elemento, devemos apenas retorná-lo. 22 | 23 | Agora, no terceiro pattern é onde a ação acontece. Nós usamos pattern matching para dividir a lista em 24 | primeiro elemento e resto. Usamos também uma associação where para definir [code]maxTail[/code] como 25 | o maior do resto da lista. Então nós testamos se o primeiro é maior que maxTail. Se for, 26 | nós retornamos ele. Se não, retornamos maxTail. 27 | 28 | Peguemos o exemplo de uma lista de números e vejamos como isso funcionaria neles: [code][2,5,1][/code]. 29 | Se chamarmos [code]maximum'[/code] com ela, os primeiros dois patterns não vão ser encontrados. 30 | O terceiro irá e a lista então será dividida em [code]2[/code] e [code][5,1][/code]. A cláusula 31 | where tem o objetivo de descobrir o maior de [code][5,1][/code], e então nós seguimos por aí. 32 | Ela testa o terceiro pattern novamente e [code][5,1][/code] é dividido em [code]5[/code] e 33 | [code][1][/code]. Novamente, a cláusula [code]where[/code] quer saber o maior de [code][1][/code]. 34 | Como essa é a condição limite, ela retorna [code]1[/code]. Finalmente! Então voltando um grau de 35 | execução, comparamos [code]5[/code] com o maior de [code][1][/code] (que é [code]1[/code]) e 36 | chegamos a [code]5[/code]. Então agora já sabemos que o maior de [code][5,1][/code] é [code]5[/code]. 37 | Subimos novamente, onde tínhamos [code]2[/code] e [code][5,1][/code]. Comparando [code]2[/code] com o 38 | maior de [code][5,1][/code] ([code]5[/code]), nós escolhemos o [code]5[/code]. 39 | 40 | Uma forma ainda mais clara de escrever essa função seria com [code]max[/code]. Caso se lembre dela, 41 | [code]max[/code] é uma função que pega dois números e retorna o maior deles. Aqui está como nós 42 | poderíamos reescrever [code]maximum'[/code] usando [code]max[/code]: 43 | 44 | Quanta sofisticação! Em essência, o maior elemento de uma lista é o maior entre o primeiro e o maior 45 | do resto da lista. 46 | -------------------------------------------------------------------------------- /cap08-criando-tipos-typeclasses/cap08-part08.md: -------------------------------------------------------------------------------- 1 | Uma typeclass sim-não 2 | ===================== 3 | 4 | Em JavaScript e outras linguagens fracamente tipadas, você pode pôr quase qualquer coisa dentro de uma expressão. Por exemplo, você pode fazer tudo o que se segue: [code]if (0) alert("YEAH!") else alert("NO!")[/code], [code]if ("") alert ("YEAH!") else alert("NO!")[/code], [code]if (false) alert("YEAH") else alert("NO!)[/code], etc. e todos esses vão mandar um alerta de [code]NO![/code]. Se você fizer [code]if ("WHAT") alert ("YEAH") else alert("NO!")[/code], vai alertar um [code]"YEAH!"[/code] porque JavaScript considera palavras não vazias como tendo um valor meio verdadeiro. 5 | 6 | Mesmo que o uso estrito de [code]Bool[/code] para semânticas booleanas funcione melhor em Haskell, vamos tentar implementar aquele comportamento meio JavaScript. Por diversão! Vamos começar com uma declaração class. 7 | 8 | 9 | Muito simples. A typeclass [code]YesNo[/code] define uma função. Tal função pega um valor de um tipo que é considerado conter algum valor de verdade e nos diz com certeza se é verdadeiro ou não. Note que da forma como utilizamos [code]a[/code] na função, [code]a[/code] tem que ser um tipo concreto. 10 | 11 | Agora, vamos definir algumas instâncias. Para números, vamos assumir que (como em JavaScript) qualquer número que não seja 0 é verdadeiro e 0 é falso. 12 | 13 | 14 | 15 | Listas vazias (e por extensão, strings) são valores negativos, enquanto que listas não-vazias são valores verdadeiros. 16 | 17 | 18 | 19 | Note como acabamos de pôr um parâmetro de tipo [code]a[/code] lá para tornar a lista um tipo concreto, mesmo que não façamos nenhuma suposição sobre o tipo que está contido na lista. O que mais, hmm... já sei, o próprio [code]Bool[/code] guarda verdade e falsidade, e é bem óbvio qual é qual. 20 | 21 | Han? O que é [code]id[/code]? É apenas uma função padrão da biblioteca que pega um parâmetro e retorna a mesma coisa, que é o que estaríamos escrevendo de qualquer forma. 22 | 23 | Vamos tornar [code]Maybe a[/code] uma instância também. 24 | 25 | 26 | 27 | Nós não precisamos de uma restrição de classe porque não fizemos nenhuma suposição sobre o conteúdo de [code]Maybe[/code]. Apenas dissemos que é verdadeiro se for um valor [code]Just[/code] e falso se for um [code]Nothing[/code]. Ainda tivemos de escrever [code](Maybe a)[/code] ao invés de apenas [code]Maybe[/code] porque, se você parar pra pensar, uma função [code]Maybe -> Bool[/code] não pode existir (porque [code]Maybe[/code] não é um tipo concreto), onde que [code]Maybe a -> Bool[/code] está bem e elegante. Ainda sim, isso é muito legal porque, agora, qualquer tipo na forma [code]Maybe something[/code] é parte de [code]YesNo[/code] e não importa o que [code]something[/code] é. 28 | 29 | Anteriormente, definimos um tipo[code]Tree a[/code], que representava uma árvore de busca binária. Podemos dizer que uma árvore vazia é falsa e qualquer coisa que não seja vazia seja verdadeira. 30 | 31 | 32 | 33 | Um semáforo pode ter um valor de sim ou não? Certamente. Se for vermelho, você para. Se for verde, você vai. Se for amarelo? Eh, eu geralmente ultrapasso amarelos porque eu vivo para a adrenalina. 34 | 35 | 36 | Legal, agora que temos algumas instâncias, vamos brincar! 37 | 38 | 39 | 40 | Certo, funciona! Vamos fazer uma função que imita o if, mas funciona com valores [code]YesNo[/code]. 41 | 42 | 43 | Bem direto ao ponto. Precisa de um valor sim-ou-não e duas coisas. Se o valor s-m-ou-não for mais pra um sim, retorna a primeira das duas coisas, caso contário, retorna a segunda delas. -------------------------------------------------------------------------------- /cap04-sintaxe-funcoes/cap04-part04.md: -------------------------------------------------------------------------------- 1 | Deixe estar 2 | =========== 3 | 4 | Muito semelhantes às associações where, existem as let. Associações where te permitem dar valores a variáveis no fim de uma função, ao mesmo tempo que permitem que toda função a veja, incluindo guards. Associações let dão valores a funções e também são expressões, mas tem um escopo mais restrito por não serem acessíveis dentro de guards. Assim como toda construção em Haskell que permite dar valores a nomes, construções let podem ser usadas em pattern matching. Vejamos em ação! Poderíamos fazer assim uma função que nos dá a área da superfície de um cilíndro baseado em sua altura e raio: 5 | 6 | 7 | Deve-se seguir a forma [code]let <bindings> in <expression>[/code]. Os nomes definidos na parte let são acessíveis mesmo na expressão seguinte in. Como você deve ter percebido, poderíamos ter definido essa associação com um where. Perceba ainda que os nomes também estão alinhados na mesma coluna. Então qual é a diferença dos dois? Por hora, let coloca as associações antes da expressão que as usa, o contrário da where. 8 | 9 | Outra diferença é que associações let são expressões por si próprias. where são apenas construções sintáticas. Lembre-se de quando vimos que a estrutura if/else é uma expressão que pode ser enfiada em qualquer lugar? 10 | 11 | 12 | Isso também pode ser feito com associações let. 13 | 14 | 15 | Elas ainda podem ser alternativas para criação e uso de funções de escopo local: 16 | 17 | 18 | 19 | Se quisermos definir várias variáveis na mesma linha, obviamente não podemos alinhá-las em uma coluna. É por isso que também podemos separá-las por pontos e vírgulas. 20 | 21 | 22 | 23 | Você não necessariamente precisa-se colocar um ponto e vírgula depois da última associação. Como já dissemos, podemos usar pattern match em associações let. Isso é muito útil para quebrar uma tupla e dar nomes a cada componente. 24 | 25 | 26 | 27 | Também pode-se colocar associações let dentro de compreensão de listas. Vamos reescrever nosso exemplo anterior que recebe listas de pares massa-altura, usar um let e dispensar a definição de uma função auxiliar interna com where. 28 | 29 | 30 | 31 | Incluímos o let dentro da compreensão de listas como predicado, mas não com a intenção de filtrar a lista, mas sim para apenas dar nomes aos elementos. Nomes definidos num let dentro de uma compreensão de listas são visíveis à função a que pertence (o que antecede o [code]|[/code]) e a todos os predicados e seções que seguem à associação. Então simplesmente podemos fazer nossa função retornar apenas o IMC de pessoas gordas: 32 | 33 | 34 | 35 | Só não podemos usar o nome [code]imc[/code] em [code](w, h) <- xs[/code] porque é executado antes da definição do let. 36 | 37 | Omitimos o in do let porque usamos-o em uma compreensão de listas, que já pré-define a visibilidade de nomes. No entanto, poderíamos usar let in no predicado e seus nomes serem acessíveis apenas dentro do predicado. O in ainda pode ser omitido ao definir funções e constantes diretamente no GHCI. Caso o façamos, os nomes serão visíveis por toda a sessão interativa. 38 | 39 | Você pode estar se perguntando: Se associações let são tão perfeitas, porque não usá-las ao invés de where? Bom, já que associações let são mais restritivas quanto ao escopo, elas não podem ser usadas entre guards. Algumas pessoas preferem usar where porque os nomes vêm depois da função que os usam. Desse modo, o corpo da função fica próximo de nomes e declaração de tipo, tornando mais legível. 40 | 41 | -------------------------------------------------------------------------------- /cap14-zippers/cap14-part02.md: -------------------------------------------------------------------------------- 1 | Caminhando 2 | ========== 3 | 4 | Como nós aprendemos na aula de biologia, existem muitos tipos de árvores, então vamos pegar a semente que usaremos para plantar a nossa. Aqui está: 5 | 6 | Então nossa árvore ou está vazia ou contém um nó que tem um elemento e duas sub-árvores. Aqui está um ótimo exemplo de uma árvore, a qual eu estou dando para você, caro leitor, de graça! 7 | 8 | E aqui está a representação gráfica da árvore: 9 | 10 | Notou o [code]W[/code] na árvore? Digamos que nós queremos muda-lo para um [code]P[/code]. Como é que vamos fazer isso? Bem, uma forma seria percorrendo nossa árvore até acharmos um elemento que seja localizado indo primeiramente para direita e depois para esquerda e mudando esse elemento. Aqui está o código para fazer isso: 11 | 12 | Eca! Não só isso é feio, mas também bastante confuso. O que aconteceu aqui? Bem, nós percorremos nossa árvore a partir do elemento raiz [code]x[/code] (que vem a ser o elemento [code]‘P’[/code] na raiz) e sua sub-árvore [code]l[/code]. Ao invés de nomear a sub-árvore direita, nós continuamos percorrendo a árvore. Nós continuamos percorrendo até atingirmos a súb-árvore na qual a raiz é o [code]‘W’[/code]. Uma vez feito isso, nós reconstruímos a árvore mudando a sub-árvore que contém o [code]‘W’[/code] como raiz mas agora alterando seu valor para [code]‘P’[/code]. 13 | 14 | Existe uma maneira melhor de fazer isso? Que tal nós fazermos nossa função usar a árvore juntamente com uma lista de direções. As direções irão ser ou [code]E[/code] ou [code]D[/code], representando esquerda ou direita respectivamente. Com isso iremos alterar o elemento que alcançarmos ao seguir as direções fornecidas. Aqui está: 15 | 16 | Se o primeiro elemento na nossa lista de direções for [code]E[/code], nós construímos uma nova árvore que seja parecida com a antiga, só se diferenciando por ter na sub árvore esquerda um elemento alterado para [code]‘P’[/code]. Quando chamamos recursivamente [code]chageToP[/code], nós passamos só a cauda da lista de direções, pois nós já fomos para esquerda. Nós fazemos a mesma coisa em caso da direção ser [code]D[/code]. Se a lista de direções estiver vazia, isso significa que nós estamos no destino, então retornamos uma árvore parecida com a que foi passada, porém tem [code]‘P’[/code] como valor da raiz. 17 | 18 | Para evitar imprimir a árvore toda, vamos fazer uma função que receba uma lista de direções e retorne qual elemento é alcançado pela lista: 19 | 20 | Essa função é bastante similar a [code]changeToP[/code], só se difere que ao invés de guardar todo caminho ao longo das direções, ela ignora tudo exceto o destino. Aqui nós mudamos o elemento [code]‘W’[/code] para [code]‘P’[/code] e ver a mudança na nossa nova árvore: 21 | 22 | Legal, parece que funciona. Nessas funções, a lista de direções age com foco, por isso conseguimos exatamente a sub-árvore desejada. Uma lista de direções com [code][D][/code] foca na sub-árvore que está a direita da raiz, por exemplo. Uma lista de direções vazia foca na árvore principal como um todo. 23 | 24 | Enquanto essa técnica parece legal, pode ser uma pouco ineficiente, especialmente quando queremos mudar os elementos várias vezes. Digamos que tenhamos um árvore realmente grande e uma longa lista de direções que aponta para algum elemento que está localizado na base da árvore. Nós usamos nossa lista de direções para caminhar ao longo da árvore e mudar o elemento da base. Se nós quiséssemos mudar outro elemento que está próximo ao elemento que acabamos de mudar, nós temos que começar tudo de novo a partir da raiz e percorrer o mesmo caminho novamente! Que saco! 25 | 26 | Na próxima seção, nós iremos encontrar uma alternativa melhor para focar em um sub-árvore, uma que permita uma troca eficiente de foco em sub-árvores vizinhas. -------------------------------------------------------------------------------- /cap04-sintaxe-funcoes/cap04-part02.md: -------------------------------------------------------------------------------- 1 | Guardas, guardas! 2 | ================= 3 | 4 | Enquanto patterns é um jeito de ter certeza de que um valor tenha a forma desejada e torna possível seu desmembramento, guards (guardas) são uma forma de testar se uma (ou mais) propriedades são verdadeiras ou falsas. Se isso te parece muito com um if, está no caminho certo. A diferença é de guards serem mais fáceis de ler em caso de muitas condições além de funcionar com patterns. 5 | 6 | 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) 7 | 8 | 9 | Guards são sinalizados por pipes, seguidos do nome de uma função e seus parâmetros. Geralmente, são alinhados e identados à direita. Um guard geralmente é uma expressão booleana. Se for [code]True[/code], a função correspondente é executada. Se [code]False[/code], o resto da linha não é executada e é passado para a próxima. Se chamarmos essa função com [code]24.3[/code], será testado se é menor ou igual a [code]18.5[/code]. Já que não é, o próximo guard é testado. A verificação obtém sucesso ao passar pelo segundo guard, já que 24,3 é menor que 25,0. Assim, é retornado o resultado do segundo guard. 10 | 11 | Os guards são remanescentes das árvores encadeadas de if/else da linguagens imperativas, mas muito mais legíveis. Apesar de árvores if/else serem extremamente desaconselhadas, às vezes um problema é definido de um modo que não há muitas alternativas. Em Haskell, Guards são uma solução. 12 | 13 | Em vários casos, o último guard é [code]otherwise[/code]. [code]otherwise[/code] é definido com [code]otherwise = True[/code] 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 [code]False[/code] (e não tivermos especificado um guard [code]otherwise[/code]), é 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. 14 | 15 | E é claro que podemos usar guards com funções que recebam quantos parâmetros desejarmos. Ao invés de obrigarmos o 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. 16 | 17 | 18 | 19 | Vamos descobrir se eu estou gordo... 20 | 21 | 22 | 23 | Ei! Não estou gordo! Mas Haskell me chamou de feio mesmo assim. Que seja. 24 | 25 | Veja que não há um [code]=[/code] depois do nome da função e seus parâmetros, mas antes do primeiro guard. Muitos iniciantes recebem erros de sintaxe ao colocarem aí. 26 | 27 | Mais uma outra bem simples: vamos implementar nosso próprio [code]max[/code]. Se não lembra, ele recebe dois parâmetros e retorna o maior. 28 | 29 | 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 [code]max'[/code] assim: 30 | 31 | 32 | Ugh! Nem um pouco legível! Próximo: nossa própria imprementação de [code]compare[/code] usando guards. 33 | 34 | 35 | 36 | 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 ler. 37 | -------------------------------------------------------------------------------- /cap14-zippers/cap14-part04.md: -------------------------------------------------------------------------------- 1 | Focando nas listas 2 | ================== 3 | 4 | Zippers também podem ser usados como qualquer outra estrutura de dados, o que não é nenhuma surpresa 5 | já que eles podem ser usados para focar em sub-listas de listas. Afinal de contas, listas são bastante 6 | parecidas com árvores, apenas quando um nó em uma árvore tem um elemento (ou não) e várias sub-árvore, 7 | um nó em uma lista tem um elemento e apenas uma única sub-lista. Quando nós implementamos a nossa própria lista, 8 | definimos nosso tipo de dados da seguinte forma: 9 | 10 | Compare isso com a nossa definição de árvore binária e será fácil ver como listas podem ser vistas 11 | árvores quando cada nó tem somente uma única sub-árvore. 12 | 13 | Uma lista como [code][1,2,3][/code] pode ser escrita como [code]1:2:3:[][/code]. Contendo a cabeça 14 | da lista, que será [code]1[/code] e então a cauda da lista, que será [code]2:3:[][/code]. 15 | Por sua vez, [code]2:3:[][/code] também tem a sua cabeça, que é [code]2[/code] e uma cauda, que é 16 | [code]3:[][/code]. Com [code]3:[][/code], o [code]3[/code] é a cabeça e a cauda é a lista vazia [code][][/code]. 17 | 18 | Vamos fazer um zipper para listas. Para mudar o nosso foco de sub-listas em listas, nós nos movemos 19 | para frente ou para trás (enquanto que em árvores nos movemos para esquerda ou para direita). A parte 20 | focada será uma sub-árvore e juntos com isso nós vamos deixar migalhas de pão à medida que avançamos. 21 | Agora, em que será que consiste uma migalha de pão para uma lista? Quando estamos lidando com árvores 22 | binária, dizemos que a migalha de pão terá que segurar o elemento na raiz do nó pai, juntamente com 23 | todas as sub-árvores que não escolhemos. Ele também terá que lembrar se fomos para esquerda ou para 24 | direita. Então, ele terá que ter todas as informações que um nó tem exceto para a sub-árvore que escolheu para se 25 | concentrar. 26 | 27 | Listas são mais simples que árvores, então não precisamos lembrar se fomos para esquerda ou direita, 28 | isso porque existe somente uma forma de ir mais fundo em listas. Como há somente uma única sub-árvore 29 | em cada nó, não precisamos lembrar do caminho que fizemos para chegar lá. E ao que tudo indica, tudo 30 | o que precisamos lembrar é do elemento anterior. Se temos uma lista como [code][3,4,5][/code] e 31 | sabemos que o elemento anterior é [code]2[/code], então nós podemos voltar apenas inserindo aquele 32 | elemento na cabeça de nossa lista, obtendo assim [code][2,3,4,5][/code]. 33 | 34 | Como uma migalha de pão aqui é apenas um elemento, não precisamos necessariamente colocar ela 35 | dentro de um tipo de dados, da forma como fizemos antes com o tipo de dados [code]Crumb[/code] para a árvore 36 | de zippers: 37 | 38 | A primeira lista representa a lista em que estamos focando e a segunda lista representa a migalha de pão. 39 | Vamos criar uma função que vai pra frente e para trás dentro das listas: 40 | 41 | Quando estamos indo para frente, nós focamos na cauda da lista atual e deixamos a cabeça do elemento 42 | como uma migalha de pão. Quando nos movemos para trás, nós pegamos a última migalha de pão e colocamos 43 | ela no inicio da lista. 44 | 45 | Aqui esta as duas funções em ação: 46 | 47 | Percebemos que as migalha de pão no caso de listas são nada mais do que apenas a parte reversa da nossa 48 | lista. O elemento que nos afastamos sempre vai para a cabeça da migalha de pão, por isso que é fácil 49 | se mover para trás apenas pegando elemento da cabeça da nossa migalha e usando isso como nosso foco. 50 | 51 | Isso também torna mais fácil ver por que chamamos isso de zíper, porque isso realmente se parece 52 | com um ziper que se move para cima e para baixo. 53 | 54 | Se você estiver fazendo um editor de texto, você pode usar uma lista de strings para representar 55 | as linhas de textos que estão atualmente abertas e você pode então usar o zipper para saber 56 | em qual linha o cursor esta atualmente focado. Ao usar um zipper, pode ser mais fácil também inserir 57 | uma linha em qualquer lugar do texto ou deletar alguma. 58 | -------------------------------------------------------------------------------- /cap13-pouco-mais-de-monads/cap13-part05.md: -------------------------------------------------------------------------------- 1 | Error error on the wall 2 | ======================= 3 | 4 | We know by now that [code]Maybe[/code] is used to add a context of possible failure to values. A value can be a [code]Just something[/code] or a [code]Nothing[/code]. However useful it may be, when we have a [code]Nothing[/code], all we know is that there was some sort of failure, but there's no way to cram some more info in there telling us what kind of failure it was or why it failed. 5 | 6 | The [code]Either e a[/code] type on the other hand, allows us to incorporate a context of possible failure to our values while also being able to attach values to the failure, so that they can describe what went wrong or provide some other useful info regarding the failure. An [code]Either e a[/code] value can either be a [code]Right[/code] value, signifying the right answer and a success, or it can be a [code]Left[/code] value, signifying failure. For instance: 7 | 8 | This is pretty much just an enhanced [code]Maybe[/code], so it makes sense for it to be a monad, because it can also be viewed as a value with an added context of possible failure, only now there's a value attached when there's an error as well. 9 | 10 | Its [code]Monad[/code] instance is similar to that of [code]Maybe[/code] and it can be found in [code]Control.Monad.Error[/code]: 11 | 12 | [code]return[/code], as always, takes a value and puts it in a default minimal context. It wraps our value in the [code]Right[/code] constructor because we're using [code]Right[/code] to represent a successful computation where a result is present. This is a lot like [code]return[/code] for [code]Maybe[/code]. 13 | 14 | The [code]>>=[/code] examines two possible cases: a [code]Left[/code] and a [code]Right[/code]. In the case of a [code]Right[/code], the function [code]f[/code] is applied to the value inside it, similar to how in the case of a [code]Just[/code], the function is just applied to its contents. In the case of an error, the [code]Left[/code] value is kept, along with its contents, which describe the failure. 15 | 16 | The [code]Monad[/code] instance for [code]Either e[/code] makes an additional requirement, and that is that the type of the value contained in a [code]Left[/code], the one that's indexed by the [code]e[/code] type parameter, has to be an instance of the [code]Error[/code] type class. The [code]Error[/code] type class is for types whose values can act like error messages. It defines the [code]strMsg[/code] function, which takes an error in the form of a string and returns such a value. A good example of an [code]Error[/code] instance is, well, the [code]String[/code] type! In the case of [code]String[/code], the [code]strMsg[/code] function just returns the string that it got: 17 | 18 | But since we usually use [code]String[/code] to describe the error when using [code]Either[/code], we don't have to worry about this too much. When a pattern match fails in [code]do[/code] notation, a [code]Left[/code] value is used to signify this failure. 19 | 20 | Anyway, here are a few examples of usage: 21 | 22 | When we use [code]>>=[/code] to feed a [code]Left[/code] value to a function, the function is ignored and an identical [code]Left[/code] value is returned. When we feed a [code]Right[/code] value to a function, the function gets applied to what's on the inside, but in this case that function produced a [code]Left[/code] value anyway! 23 | 24 | When we try to feed a [code]Right[/code] value to a function that also succeeds, we're tripped up by a peculiar type error! Hmmm. 25 | 26 | Haskell says that it doesn't know which type to choose for the [code]e[/code] part of our [code]Either e a[/code] typed value, even though we're just printing the [code]Right[/code] part. This is due to the [code]Error e[/code] constraint on the [code]Monad[/code] instance. So if you get type errors like this one when using [code]Either[/code] as a monad, just add an explicit type signature: 27 | 28 | Alright, now it works! 29 | 30 | Other than this little hangup, using this monad is very similar to using [code]Maybe[/code] as a monad. In the previous chapter, we used the monadic aspects of [code]Maybe[/code] to simulate birds landing on the balancing pole of a tightrope walker. As an exercise, you can rewrite that with the error monad so that when the tightrope walker slips and falls, we remember how many birds were on each side of the pole when he fell. -------------------------------------------------------------------------------- /cap12-punhado-de-monads/cap12-part03.md: -------------------------------------------------------------------------------- 1 | O tipo de classe Monad 2 | ====================== 3 | 4 | Assim como functors tem o tipo de classe [code]Functor[/code] e applicative functors tem o tipo de classe [code]Applicative[/code], monads tem seu próprio tipo de classe: [code]Monad[/code]! Wow, quem teria adivinhado? Assim é como esse tipo de classe se parece: 5 | 6 | Vamos começar com a primeira linha. Ela diz: [code]class Monad m where[/code]. Mas espere, nós não dizemos que monads são apenas reforços de applicative functors? Não deveria haver uma restrição de classe ao longo das linhas de [code]class (Applicative m) = > Monad m where[/code] devendo assim o tipo ser primeiro um applicative functor antes de produzir um monad? Bem, não deveria, mas quando Haskell foi criado, não tinha ocorrido ser uma boa opção para Haskell. Mas com certeza, cada monad é um applicative functor, mesmo se a declaração não dizer isso. 7 | 8 | A primeira função que o tipo de classe [code]Monad[/code] define é [code]return[/code]. É o mesmo que [code]pure[/code], somente com um nome diferente. Esse tipo é [code](Monad m) => a -> m a[/code]. É preciso um valor para colocá-lo em um contexto padrão mínimo que ainda mantém esse valor. Em outras palavras, é preciso alguma coisa para envolver ele em um monad. Ele sempre faz a mesma coisa com a função [code]pure[/code] para o tipo de classe [code]Applicative[/code], o que significa que já estamos familiarizados com [code]return[/code]. Nós já usamos [code]return[/code] quando fizemos I/O. Usamos ele para pegar um valor e fazer uma ação fictícia de I/O que não faz nada mas que produz um valor. Para [code]Maybe[/code] ele cria um valor e envolve ele em um [code]Just[/code]. 9 | 10 | Apenas um lembrete: [code]return[/code] não tem nada a ver com [code]return[/code] que é usado na maioria das outras linguagens. Ele não termina a execução da função ou qualquer coisa assim, ele apenas tem um valor normal e coloca ele em um contexto. 11 | 12 | A próxima função é [code]>>=[/code], ou bind. É como um function application, só que em vez de ter um valor normal e alimentar ele para uma função normal, ele tem um valor monadico (ou seja, um valor com um contexto) e alimenta ele para uma função que recebe um valor normal, mas retorna um valor monadico. 13 | 14 | Em seguida, temos [code]>>[/code]. Nós não vamos prestar muita atenção nisso agora porque ele vem com uma implementação padrão e nós praticamente nunca implementamos isso ao criar instâncias de monads. 15 | 16 | A última função do tipo de classe [code]Monad[/code] é [code]fail[/code]. Nós nunca usamos ela explicitamente em nosso código. Em vez disso, ela é usada pelo Haskell para permitir falhas em um construtor semântico especial para monads que iremos conhecer depois. Nós não precisamos nos preocupar com [code]fail[/code] por hora. 17 | 18 | 19 | Agora que nós sabemos como que o tipo de classe [code]Monad[/code] se parece, vamos dar uma olhada em como [code]Maybe[/code] é uma instância de [code]Monad[/code]! 20 | 21 | [code]return[/code] é o mesmo que [code]pure[/code], assim não nos exige esforços. Nós fazemos o mesmo que fizemos no tipo de classe [code]Applicative[/code] e envolvemos ele em um [code]Just[/code]. 22 | 23 | A função [code]>>=[/code] é o mesmo que o nosso [code]applyMaybe[/code]. Quando alimentamos o [code]Maybe a[/code] para nossa função, nós temos em mente o contexto e retornamos um [code]Nothing[/code] se o valor da esquerda é [code]Nothing[/code] porque se não há nenhum valor, então não há nenhuma maneira de aplicar a nossa função nele. Se é um [code]Just[/code] pegamos o que está dentro da função e aplicamos [code]f[/code]. 24 | 25 | Nós podemos brincar por ai com [code]Maybe[/code] como sendo um monad: 26 | 27 | Nada de novo ou emocionante na primeira linha uma vez que já usamos [code]pure[/code] com [code]Maybe[/code] e nós sabemos que [code]return[/code] é apenas [code]pure[/code] com um nome diferente. As próximas duas linhas [code]>>=[/code] mostram um pouco mais. 28 | 29 | Note que quando nós oferecemos [code]Just 9[/code] para a função [code]\x -> return (x*10)[/code], o [code]x[/code] assumiu o valor [code]9[/code] dentro da função. Parece que fomos capazes de extrair o valor de [code]Maybe[/code] sem pattern-matching. E nós ainda não perdemos o contexto do nosso valor [code]Maybe[/code], porque quando ele é [code]Nothing[/code], o resultado do uso [code]>>=[/code] vai ser [code]Nothing[/code] também. -------------------------------------------------------------------------------- /cap14-zippers/cap14-part06.md: -------------------------------------------------------------------------------- 1 | Watch your step 2 | =============== 3 | 4 | So far, while walking through our data structures, whether they were binarytrees, lists or file systems, we didn't really care if we took a step too farand fell off. For instance, our [code]goLeft[/code] function takesa zipper of a binary tree and moves the focus to its left sub-tree: 5 | 6 | But what if the tree we're stepping off from is an empty tree? That is, what ifit's not a [code]Node[/code], but an [code]Empty[/code]?In this case, we'd get a runtime error because the pattern match would fail andwe have made no pattern to handle an empty tree, which doesn't have anysub-trees at all. So far, we just assumed that we'd never try to focus on theleft sub-tree of an empty tree as its left sub-tree doesn't exist at all. Butgoing to the left sub-tree of an empty tree doesn't make much sense, and so farwe've just conveniently ignored this. 7 | 8 | Or what if we were already at the root of some tree and didn't have anybreadcrumbs but still tried to move up? The same thing would happen. It seemsthat when using zippers, any step could be our last (cue ominous music). Inother words, any move can result in a success, but it can also result in afailure. Does that remind you of something? Of course, monads! Morespecifically, the [code]Maybe[/code] monad which adds a context ofpossible failure to normal values. 9 | 10 | So let's use the [code]Maybe[/code] monad to add a context ofpossible failure to our movements. We're going to take the functions that workon our binary tree zipper and we're going to make them into monadic functions.First, let's take care of possible failure in [code]goLeft[/code]and [code]goRight[/code]. So far, the failure of functions thatcould fail was always reflected in their result, and this time is nodifferent. So here are [code]goLeft[/code] and[code]goRight[/code] with an added possibility of failure: 11 | 12 | Cool, now if we try to take a step to the left of an empty tree, we get a[code]Nothing[/code]! 13 | 14 | Looks good! How about going up? The problem before happened if we tried to go up butwe didn't have any more breadcrumbs, which meant that we were already in theroot of the tree. This is the [code]goUp[/code] function thatthrows an error if we don't keep within the bounds of our tree: 15 | 16 | Now let's modify it to fail gracefully: 17 | 18 | If we have breadcrumbs, everything is okay and we return a successful new focus,but if we don't, then we return a failure. 19 | 20 | Before, these functions took zippers and returned zippers, which meant that wecould chain them like this to walk around: 21 | 22 | But now, instead of returning [code]Zipper a[/code], they return [code]Maybe (Zipper a)[/code], so chaining functions like thiswon't work. We had a similar problem when wewere dealing with our tightropewalker in the chapter about monads. He also walked one step at a time andeach of his steps could result in failure because a bunch of birds could land onone side of his balancing pole and make him fall. 23 | 24 | Now, the joke's on us becausewe're the ones doing the walking, and we're traversing a labyrinth of our owndevising. Luckily, we can learn from the tightrope walker and just do what hedid, which is to exchange normalfunction application for using [code]>>=[/code], which takesa value with a context (in our case, the [code]Maybe (Zipper a)[/code], which has a context of possible failure) and feeds it into a function whilemaking sure that the context is taken care of. So just like our tightrope walker, we're going to trade in all our [code]-:[/code] operators for[code]>>=[/code]. Alright, we can chain our functions again!Watch: 25 | 26 | We used [code]return[/code] to put a zipper in a [code]Just[/code] and then used [code]>>=[/code] tofeed that to our [code]goRight[/code] function. First, we made atree that has on its left an empty sub-tree and on its right a node that has twoempty sub-trees. When we try to go right once, the result is a success, becausethe operation makes sense. Going right twice is okay too; we end up with thefocus on an empty sub-tree. But going right three times wouldn't make sense,because we can't go to the right of an empty sub-tree, which is why the result is a [code]Nothing[/code]. 27 | 28 | Now we've equipped our trees with a safety-net that will catch us shouldwe fall off. Wow, I nailed this metaphor. 29 | 30 | Our file system also has a lot of cases where an operation could fail, such astrying to focus on a file or folder that doesn't exist. As an exercise, you canequip our file system with functions that fail gracefully by using the [code]Maybe[/code] monad. -------------------------------------------------------------------------------- /cap07-modulos/cap07-part06.md: -------------------------------------------------------------------------------- 1 | Fazendo seus próprios módulos 2 | ============================= 3 | 4 | Temos visto vários módulos interessantes, mas como fazemos o nosso próprio? Quase toda linguagem 5 | de programação permite dividir o código em vários arquivos e Haskell não é diferente. Ao escrever 6 | programas, uma boa prática é juntar funções e tipos que tem o mesmo propósito num módulo. Desse modo, 7 | você pode facilmente reutilizar funções de outros programas simplesmente importando módulos. 8 | 9 | Vamos aprender como criar um módulo tendo como exemplo algumas funções de cálculo de volume e área de objetos 10 | geométricos. Podemos criar um arquivo chamado [code]Geometry.hs[/code]. 11 | 12 | Dizemos que um módulo exporta funções. Ao importar um módulo, eu passo a poder usar as funções 13 | que foram exportadas. Um módulo também pode definir funções que possuem chamadas a funçoes internas, 14 | mas só podemos ver e usar as explicitamente exportadas. 15 | 16 | No início de um módulo, especificamos seu nome. Se nosso arquivo se chama [code]Geometry.hs[/code], 17 | nosso módulo deve se chamar [code]Geometry[/code]. Especificamos as funções que serão exportadas e 18 | então podemos começar a escrever nossas próprias funções. 19 | 20 | Como pode ver, calcularemos área e volume de esferas, cubos e prismas. Enfim, as próprias definições: 21 | 22 | 23 | A boa e velha geometria. Mas temos algumas coisas a atentar. Como um cubo nada mais é do que um caso 24 | especial de prisma, definimos sua área e volume como um prisma com todas medidas iguais. Ainda definimos 25 | uma função auxiliar chamada [code]rectangleArea[/code], que calcula a área de um retângulo baseado 26 | nas medidas dos seus lados. Extremamente simples já que é pura multiplicação. Perceba que apesar de a 27 | usarmos (como também [code]cuboidArea[/code] e [code]cuboidVolume[/code]) no módulo, não a 28 | exportamos! Isso é devido ao módulo tratar-se apenas de funções de objetos tridimensionais (mas que não 29 | deixam de precisar da [code]rectangleArea[/code]). 30 | Ao criar um módulo, geralmente exportamos apenas as funções adequadas e essenciais ao seu propósito, 31 | deixando as outras internas. Se alguém estiver usando o módulo [code]Geometry[/code], não precisa se 32 | preocupar com outras funções alheias a seu interesse. Podemos decidir modificar ou mesmo deletar uma 33 | dessas funções em uma próxima versão (como deletar [code]rectangleArea[/code] e substituir por 34 | [code]*[/code]) e ninguém perceberia a mudança. 35 | 36 | Para usar o módulo, é só fazer isso: 37 | 38 | [code]Geometry.hs[/code] deve estar na mesma pasta que o programa que o importa. 39 | Módulos também podem assumir estruturas hierárquicas. Cada módulo pode ter submódulos que também podem 40 | ter seus submódulos. Vamos testar o conceito para que o [code]Geometry[/code] tenha três submódulos, 41 | um para cada tipo de objeto. 42 | 43 | Primeiro, criamos uma pasta chamada [code]Geometry[/code]. Atenção ao G maiúsculo. Nela, teremos três 44 | arquivos: [code]Sphere.hs[/code], [code]Cuboid.hs[/code], e [code]Cube.hs[/code]. O que cada arquivo 45 | conterá: 46 | 47 | [code]Sphere.hs[/code] 48 | 49 | 50 | [code]Cuboid.hs[/code] 51 | 52 | 53 | [code]Cube.hs[/code] 54 | 55 | 56 | Perfeito! O primeiro é [code]Geometry.Sphere[/code]. Veja que colocamos tudo na pasta 57 | [code]Geometry[/code] e definimos o módulo como [code]Geometry.Sphere[/code]. Fazemos o mesmo que o 58 | prisma. Perceba também que em todos submódulo, temos funções de mesmo nome. Só podemos fazer isso por 59 | se tratarem de módulos separados. Queremos usar as funções de [code]Geometry.Cuboid[/code] em 60 | [code]Geometry.Cube[/code] mas não podemos apenas dar um [code]import Geometry.Cuboid[/code] por 61 | exportar funções de mesmo nome que [code]Geometry.Cube[/code]. Por isso que fazemos uma importação 62 | qualificada e tudo funciona. 63 | 64 | Se estivesse num arquivo no mesmo nível da pasta [code]Geometry[/code], podemos, digamos: 65 | 66 | 67 | E então chamamos [code]area[/code] e [code]volume[/code] que devolverá área e volume de uma esfera. 68 | Mas se quisermos usar dois ou mais módulos, teríamos que fazer malabarismos. Ou... fazer importações 69 | qualificadas. Então só fazemos isso: 70 | 71 | Só chamar [code]Sphere.area[/code], [code]Sphere.volume[/code], [code]Cuboid.area[/code], etc. e que 72 | cada um vai calcular área ou volume do objeto correspondente. 73 | 74 | Logo que você se ver em um arquivo muito grande e com várias funções, tente perceber quais funções 75 | tem um propósito em comum e estudar se pode colocá-las num módulo separado. Você pode simplesmente 76 | importá-lo na próxima vez que um programa necessitar da mesma funcionalidade. 77 | -------------------------------------------------------------------------------- /cap12-punhado-de-monads/cap12-part01.md: -------------------------------------------------------------------------------- 1 | Um punhado de Monads 2 | ==================== 3 | 4 | Quando nós começamos a falar sobre functors, vimos que elas foram conceitos úteis para os valores que poderiam ser mapeados. Então, nós tornamos esse conceito um passo a mais atrávez da introdução da applicative functors, o que nos permite ver certos tipos de valores como valores com contexto e utilizar funções normais nesses valores enquanto preserva-se o significado desses conceitos. 5 | 6 | Neste capítulo, nós vamos estudar sobre monads, que apenas reforça applicative functors. Muito semelhante a applicative functors que somente reforça functors. 7 | 8 | Quando nós começamos com functors, vimos que é possível mapear funções de vários tipos de dados. Nós vimos que para esse propósito, o tipo de classe [code]Functor[/code] nos foi apresentado o que nos levou a fazer a seguinte pergunta: quando temos um tipo de função [code]a -> b[/code] e também um tipo de dado [code]f a[/code], como nós fazemos para mapear essa função para terminar com um tipo de dado [code]f b[/code]? Nós vimos como mapear somente um [code]Maybe a[/code], uma lista [code][a][/code], um [code]IO a[/code] etc. Nós até vimos como mapear uma função [code]a -> b[/code] para outra função do tipo [code]r -> a[/code] para obter uma função do tipo [code]r -> b[/code]. Para responder essa pergunta de como mapear uma função para algum tipo de dado, tudo o que nós temos que fazer é olhar para o tipo de dado [code]fmap[/code]: 9 | 10 | E então fizemos funcionar para nossos tipos de dados escrevendo instâncias apropriadas de [code]Functor[/code]. 11 | 12 | Em seguida vimos uma possibilidade de melhorar as functors e dizer, ei, e se essa função [code]a -> b[/code] já estiver empacotada dentro de um functor value? Por exemplo, e se nós tivermos [code]Just (*3)[/code], como nós fazemos para aplicar isso a [code]Just 5[/code]? E se nós não quisermos aplicar para [code]Just 5[/code] mas em vez disso para um [code]Nothing[/code]? Ou se nós temos [code][(*2),(+4)][/code] como podemos aplicar isso a [code][1,2,3][/code]? Como isso funciona mesmo? Por isso, o tipo de classe [code]Applicative[/code] foi introduzido, no qual queremos a resposta para o seguinte tipo: 13 | 14 | Vimos também que podemos ter um valor normal e empacota-lo dentro de um tipo de dado. Por exemplo, podemos ter [code]1[/code] e empacota-lo de modo a se tornar um [code]Just 1[/code]. Ou podemos transforma-lo em [code][1][/code]. Ou em uma ação de I/O que não faz nada e apenas produz [code]1[/code]. A função que faz isso é chamada de [code]pure[/code]. 15 | 16 | Como dissemos, um valor de aplicativo pode ser visto como um valor com um contexto adicional. Um valor imaginário, incluído em termos técnicos. Por exemplo, o caractere [code]'a'[/code] é apenas um caractere normal, enquanto que [code]Just 'a'[/code] tem algum contexto adicionado. Em vez de um [code]Char[/code], nós temos um [code]Maybe Char[/code], que nos diz que o valor pode ser um caractere, mas também pode ser uma ausência de um caractere. 17 | 18 | Foi ótimo ver como o tipo de classe [code]Applicative[/code] nos permitiu usar funções normais nesses valores com contexto e como que o contexto foi preservado. Observe: 19 | 20 | Ah, legal, agora que tratamos eles como valores de aplicativo, [code]Maybe a[/code] representa valores calculados que podem ter falhado, [code][a][/code] representa valores calculados que tem vários resultados (cálculos não determinísticos), [code]IO a[/code] são valores que representam valores que tem efeitos colaterais, etc. 21 | 22 | Monads são extensões naturais de applicative functors e com elas estamos preocupados com isto: Se você tem um valor com um contexto, [code]m a[/code], como você aplica isto a uma função que tem um contexto normal [code]a[/code] e retorna um valor com um contexto? Quer dizer, como você aplica uma função do tipo [code]a -> m b[/code] para um valor do tipo [code]m a[/code]? Assim, basicamente nós vamos querer essa função: 23 | 24 | Se você tem um valor imaginário e uma função que tem um valor normal mas retorna um valor imaginário, como fazemos para alimentar este valor imaginário em uma função? Esta é a principal questão que vamos nos preocupar quando estivermos lidando com monads. Nós escrevemos [code]m a[/code] em vez de [code]f a[/code] porque o [code]m[/code] significa [code]Monad[/code], mas monads são apenas applicative functors que suportam [code]>>=[/code]. A função [code]>>=[/code] é pronunciada como bind. 25 | 26 | Quando nós temos um valor normal [code]a[/code] e uma função normal [code]a -> b[/code] é realmente fácil alimentar o valor da função - você apenas aplica a função com o valor que vem com determinado contexto, é preciso um pouco de pensamento para ver como esses valores imaginários são alimentados em funções e como levam em conta o seu comportamento, mas você vai ver como é fácil como um, dois três. -------------------------------------------------------------------------------- /cap02-comecando/cap02-part05.md: -------------------------------------------------------------------------------- 1 | Eu sou a compreensão de lista 2 | ============================= 3 | 4 | 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, [code]x[/code] é a variável, [code]N[/code] é o input e [code]x <= 10[/code] o predicado. Isso significa que o conjunto contém o dobro de todos os números naturais que satisfazem o predicado. 5 | 6 | Se quisermos escrever isto em Haskell nós podemos fazer algo como [code]take 10 [2,4..][/code]. Mas se nós não quisermos dobrar 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 é [code][x*2 | x <- [1..10]][/code]. [code]x[/code] é traçado a partir de [code][1..10][/code] e para cada elemento em [code][1..10][/code] (que temos vinculado a [code]x[/code]) teremos esse elemento dobrado. Aqui esta a compreensão em ação. 7 | 8 | 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. 9 | 10 | 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. 11 | 12 | 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 [code]"BANG!"[/code] e cada número ímpar menor do que 10 com [code]"BOOM!"[/code]. 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. 13 | 14 | A última parte da compreensão é o predicado. A função [code]odd[/code] retorna [code]True[/code] quando o número for ímpar e [code]False[/code] quando for par. O elemento é incluído na lista somente se todos os predicados examinados sejam [code]True[/code]. 15 | 16 | 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: 17 | 18 | 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 filtrado. Se eu tiver duas listas, [code][2,5,10][/code] e [code][8,10,11][/code] e quiser obter o produto de todas as combinações possíveis entre números destas listas, aqui esta o que devemos fazer. 19 | 20 | Como esperado, o length da nova lista é 9. E se eu quiser todos os possíveis produtos que sejam maiores do que 50? 21 | 22 | Que tal uma compreensão de lista que combina uma lista de adjetivos e uma lista de substantivos... só para dar umas risadas. 23 | 24 | Já sei! Vamos escrever a nossa própria versão do [code]length[/code]! Chamaremos isto de [code]length'[/code]. 25 | 26 | [code]_[/code] 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 [code]_[/code]. Esta função substitui todos os elementos da lista com [code]1[/code] e soma todos eles. A idéia é ter o resultado da soma e obter depois o length da nossa lista. 27 | 28 | 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. 29 | 30 | Testando isto: 31 | 32 | 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 [code]['A'..'Z'][/code]. 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. 33 | 34 | 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. -------------------------------------------------------------------------------- /cap02-comecando/cap02-part02.md: -------------------------------------------------------------------------------- 1 | Primeiras funções do seu filho 2 | ============================== 3 | 4 | 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. 5 | 6 | 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 [code]=[/code] e depois nós iremos definir o que a função faz. Salve isto como [code]baby.hs[/code] ou algo parecido. Agora vá até o local onde o arquivo foi salvo e rode o [code]ghci[/code] a partir dai. Dentro do GCHI, digite [code]:l baby[/code]. Agora o script estará carregado e nós já podemos brincar com a função que definimos. 7 | 8 | Como o [code]+[/code] 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. 9 | 10 | Simples. Nós podemos definir isso também como [code]doubleUs x y = x + x + y + y[/code]. Testar isto produzirá um resultado bastante previsível (lembre-se de anexar esta função no arquivo [code]baby.hs[/code], salva-lo e então rodar [code]:l baby[/code] dentro do GHCI). 11 | 12 | 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 [code]doubleUs[/code] da seguinte forma: 13 | 14 | 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 [code]doubleMe[/code] para ser [code]x + x + x[/code] e desde que [code]doubleUs[/code] chamasse [code]doubleMe[/code], já estaria funcionando automaticamente neste estranho mundo onde 2 é 3. 15 | 16 | Funções em Haskell não precisam estar em uma ordem em particular, portanto não importa se você definir primeiro o [code]doubleMe[/code] e depois o [code]doubleUs[/code] ou se fizer o contrário. 17 | 18 | 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. 19 | 20 | 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. [code]5[/code] é uma expressão porque isto retorna 5, [code]4 + 8[/code] é uma expressão, [code]x + y[/code] é uma expressão porque retorna a soma de [code]x[/code] e [code]y[/code]. 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. 21 | 22 | Se tivéssemos omitido os parênteses ele teria adicionado 1 somente se [code]x[/code] não fosse maior do que 100. Note o [code]'[/code] 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 [code]'[/code] 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 [code]'[/code] ser um caractere válido em funções, nós podemos fazer uma função como esta: 23 | 24 | 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, [code]conanO'Brien[/code] e a string [code]"Este sou eu, Conan O'Brien!"[/code] podem ser usadas alternadamente. -------------------------------------------------------------------------------- /cap07-modulos/cap07-part01.md: -------------------------------------------------------------------------------- 1 | Módulos 2 | ======= 3 | 4 | ** Carregando módulos ** 5 | 6 | Um módulo Haskell é uma coleção de funções, tipos e typeclasses. Um programa Haskell é uma 7 | coleção de módulos, onde o módulo principal carrega outros e usa suas funções para fazer algo de útil. 8 | Ter o código dividido em vários módulos tem várias vantagens. Se o módulo é genérico o bastante, 9 | quando exportadas, suas funções podem ser úteis a um bando de softwares. Se o seu próprio código 10 | está separado em módulos que não requerem uns aos outros (muito), eles podem ser utilizáveis em 11 | outros projetos. É uma grande vantagem perder algum tempo separando seu código em algumas partes 12 | com propósitos adequados. 13 | 14 | A biblioteca padrão Haskell é dividida em módulos, que contém tipos e funções semelhantes ou que têm 15 | um uso semelhante. Existe um módulo para manipulação de listas, um módulo para programação concorrente, 16 | um módulo para lidar com números complexos, etc. Todas as funções, tipos e typeclasses que trabalhamos 17 | até agora fazem parte do módulo [code]Prelude[/code], que já é importado automaticamente. 18 | Nesse capítulo, daremos uma olhada em alguns módulos úteis e suas funções. Mas primeiro, como importar 19 | módulos. 20 | 21 | A sintaxe de importação módulos em um script Haskell é [code]import <nome do módulo>[/code]. 22 | Isso deve ser feito antes de definir qualquer função, por isso importações são feitas geralmente no 23 | início do arquivo. Obviamente um script pode importar vários módulos, só é preciso colocar o código 24 | apropriado em linhas distintas. Então vamos importar o módulo [code]Data.List[/code] que possui 25 | várias funções úteis para se trabalhar com listas, inclusive uma que servirá para nos dizer quantos 26 | elementos únicos existem numa dada lista. 27 | 28 | 29 | Quando você dá um [code]import Data.List[/code], todas as funções de [code]import Data.List[/code] 30 | tornam-se disponíveis no namespace global, o que significa que a partir daí podemos usar qualquer uma 31 | delas no momento que quisermos. [code]nub[/code] é uma das funções definidas em [code]Data.List[/code] 32 | que recebe uma lista e retira todos os elementos repetidos. Misturar [code]length[/code] e 33 | [code]nub[/code] fazendo [code]length . nub[/code] resulta em uma função equivalente a 34 | [code]\xs -> length (nub xs)[/code]. 35 | 36 | Você também pode colocar funções de módulos no global namespace no GHCI. Se estiver no GHCI e 37 | quiser chamar uma das funções de [code]Data.List[/code], você pode fazer: 38 | 39 | 40 | Se o que você quer é carregar vários módulos dentro do GHCI, não é necessário digitar 41 | [code]:m +[/code] milhares de vezes porque há a possibilidade de fazer o mesmo em apenas uma linha. 42 | 43 | 44 | Contudo se já carregou um script que carrega módulos, não é necessário usar [code]:m +[/code] novamente. 45 | 46 | Se seu interesse é em apenas meia dúzia de funções, você pode importar apenas elas. Para carregar 47 | somente a [code]nub[/code] e a [code]sort[/code] do [code]Data.List[/code], fazemos assim: 48 | 49 | 50 | Você ainda pode importar - com exceções - todas as funções de um módulo. Isso é útil quando módulos 51 | exportam funções de mesmo nome e você não quer sofrer com conflitos. Já que já falamos da função 52 | [code]nub[/code], vamos importar todas as funções do [code]Data.List[/code] menos ela: 53 | 54 | 55 | Outro meio de lidar com conflitos de nome é qualificar importações. O módulo [code]Data.Map[/code] 56 | que provê formas de pesquisar valores por chaves em estruturas de dados, exporta várias funções de 57 | nomes repetidos como [code]Prelude[/code], [code]filter[/code] e [code]null[/code]. Então quando 58 | importamos [code]Data.Map[/code] e chamamos [code]filter[/code], Haskell não sabe qual função você quer. 59 | E é assim que resolvemos esse problema: 60 | 61 | 62 | Isso quer dizer que quando quisermos referenciar a [code]filter[/code] do [code]Data.Map[/code], 63 | temos que colocar [code]Data.Map.filter[/code], e que [code]filter[/code] continua sendo nosso 64 | [code]filter[/code] já conhecido e amado. O problema é que digitar [code]Data.Map[/code] na frente de 65 | cada função que pode dar problema é um pouco trabalhoso. Por isso que também podemos renomear 66 | importações qualificadas para algo mais curto: 67 | 68 | 69 | Agora para referenciar a [code]filter[/code] do [code]Data.Map[/code] fica apenas [code]M.filter[/code]. 70 | 71 | Leia essa ótima referência 72 | para ver quais módulos vêm na biblioteca padrão. Um bom modo de aprender algo novo de Haskell é 73 | mergulhar na biblioteca padrão para descobrir módulos e funções carregadas automaticamente. Você ainda 74 | pode ler seus códigos-fonte. Esse também é um dos melhores jeitos de aprender e adquirir um 75 | conhecimento sólido em Haskell. 76 | 77 | Para pesquisar sobre e ver a localização de funções, use o 78 | Hoogle. Nesse incrível sistema de busca de Haskell, 79 | você pode pesquisar por nome, módulo ou até mesmo declarações de tipo. 80 | -------------------------------------------------------------------------------- /cap01-introducao/cap01-part02.md: -------------------------------------------------------------------------------- 1 | Então, o que é Haskell? 2 | ======================= 3 | 4 | Haskell é uma linguagem de programação puramente funcional. 5 | 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 é 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". 6 | 7 | 8 | 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 [code] xs = [1,2,3,4,5,6,7,8] [/ code] e uma função [code] doubleMe [/ code] 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 [code] doubleMe (doubleMe (doubleMe (xs ))) [/ code], 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 [code] doubleMe [/ code] 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 [code] doubleMe [/ code] 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. 9 | 10 | 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 [code] a = 5 + 4 [/ code], você não precisará dizer para o Haskell que [código] a [/ code] é 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. 11 | 12 | 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. 13 | 14 | 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. 15 | -------------------------------------------------------------------------------- /cap13-pouco-mais-de-monads/cap13-part03.md: -------------------------------------------------------------------------------- 1 | Reader? Ugh, not this joke again. 2 | ================================= 3 | 4 | In the chapter about applicatives, we saw that the function type, [code](->) r[/code] is an instance of [code]Functor[/code]. Mapping a function [code]f[/code] over a function [code]g[/code] will make a function that takes the same thing as [code]g[/code], applies [code]g[/code] to it and then applies [code]f[/code] to that result. So basically, we're making a new function that's like [code]g[/code], only before returning its result, [code]f[/code] gets applied to that result as well. For instance: 5 | 6 | We've also seen that functions are applicative functors. They allow us to operate on the eventual results of functions as if we already had their results. Here's an example: 7 | 8 | The expression [code](+) <$> (*2) <*> (+10)[/code] makes a function that takes a number, gives that number to [code](*2)[/code] and [code](+10)[/code] and then adds together the results. For instance, if we apply this function to [code]3[/code], it applies both [code](*2)[/code] and [code](+10)[/code] to [code]3[/code], giving [code]6[/code] and [code]13[/code]. Then, it calls [code](+)[/code] with [code]6[/code] and [code]13[/code] and the result is [code]19[/code]. 9 | 10 | Not only is the function type [code](->) r[/code] a functor and an applicative functor, but it's also a monad. Just like other monadic values that we've met so far, a function can also be considered a value with a context. The context for functions is that that value is not present yet and that we have to apply that function to something in order to get its result value. 11 | 12 | Because we're already acquainted with how functions work as functors and applicative functors, let's dive right in and see what their [code]Monad[/code] instance looks like. It's located in [code]Control.Monad.Instances[/code] and it goes a little something like this: 13 | 14 | We've already seen how [code]pure[/code] is implemented for functions, and [code]return[/code] is pretty much the same thing as [code]pure[/code]. It takes a value and puts it in a minimal context that always has that value as its result. And the only way to make a function that always has a certain value as its result is to make it completely ignore its parameter. 15 | 16 | The implementation for [code]>>=[/code] seems a bit cryptic, but it's really not all that. When we use [code]>>=[/code] to feed a monadic value to a function, the result is always a monadic value. So in this case, when we feed a function to another function, the result is a function as well. That's why the result starts off as a lambda. All of the implementations of [code]>>=[/code] so far always somehow isolated the result from the monadic value and then applied the function [code]f[/code] to that result. The same thing happens here. To get the result from a function, we have to apply it to something, which is why we do [code](h w)[/code] here to get the result from the function and then we apply [code]f[/code] to that. [code]f[/code] returns a monadic value, which is a function in our case, so we apply it to [code]w[/code] as well. 17 | 18 | If don't get how [code]>>=[/code] works at this point, don't worry, because with examples we'll see how this is a really simple monad. Here's a [code]do[/code] expression that utilizes this monad: 19 | 20 | This is the same thing as the applicative expression that we wrote earlier, only now it relies on functions being monads. A [code]do[/code] expression always results in a monadic value and this one is no different. The result of this monadic value is a function. What happens here is that it takes a number and then [code](*2)[/code] gets applied to that number and the result becomes [code]a[/code]. [code](+10)[/code] is applied to the same number that [code](*2)[/code] got applied to and the result becomes [code]b[/code]. [code]return[/code], like in other monads, doesn't have any other effect but to make a monadic value that presents some result. This presents [code]a+b[/code] as the result of this function. If we test it out, we get the same result as before: 21 | 22 | Both [code](*2)[/code] and [code](+10)[/code] get applied to the number [code]3[/code] in this case. [code]return (a+b)[/code] does as well, but it ignores it and always presents [code]a+b[/code] as the result. For this reason, the function monad is also called the reader monad. All the functions read from a common source. To illustrate this even better, we can rewrite [code]addStuff[/code] like so: 23 | 24 | We see that the reader monad allows us to treat functions as values with a context. We can act as if we already know what the functions will return. It does this by gluing functions together into one function and then giving that function's parameter to all of the functions that it was glued from. So if we have a lot of functions that are all just missing one parameter and they'd eventually be applied to the same thing, we can use the reader monad to sort of extract their future results and the [code]>>=[/code]implementation will make sure that it all works out. -------------------------------------------------------------------------------- /cap02-comecando/cap02-part06.md: -------------------------------------------------------------------------------- 1 | Tuplas 2 | ====== 3 | 4 | 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. 5 | 6 | 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. 7 | 8 | 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 [code][[1,2],[8,11],[4,5]][/code]. O problema com este método é que podemos fazer algumas coisas como [code][[1,2],[8,11,5],[4,5]][/code], 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: [code][(1,2),(8,11),(4,5)][/code] E se eu quiser uma superfície como [code][(1,2),(8,11,5),(4,5)][/code]? Bem, isto me dará um erro: 9 | 10 | 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 [code][(1,2),("One",2)][/code] 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: [code]("Christopher", "Walken", 55)[/code]. Como vimos nesse exemplo, tuplas também podem conter listas 11 | 12 | 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. 13 | 14 | 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. 15 | 16 | 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: 17 | 18 | [function]fst[/code] recebe um par e retorna seu primeiro componente. 19 | 20 | [function]snd[/code] recebe um par e retorna seu segundo componente. Surpresa! 21 | 22 | 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. 23 | 24 | Uma função legal que produz uma lista de pares: [function]zip[/code]. 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 demostração: 25 | 26 | 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, [code]zip[/code] pode pegar duas listas que contém tipos diferentes e comprimir em uma só. O que acontece se os tamanhos das listas forem diferentes? 27 | 28 | 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: 29 | 30 | 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: 31 | 32 | 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 [code]triangles[/code] 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. 33 | 34 | Estamos quase terminando. Agora, acabamos de modificar a função, dizendo que queremos aqueles em que o perímetro é igual a 24. 35 | 36 | 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. -------------------------------------------------------------------------------- /cap06-funcoes-alta-ordem/cap06-part07.md: -------------------------------------------------------------------------------- 1 | Composição de funções 2 | ===================== 3 | 4 | Na matemática, composição de funções 5 | é definida como: [image " (f . g)(x) = f(g(x))"], que significa que compor duas funções produz uma nova 6 | função, quando chamamos com um parâmetro, digamos, x é o equivalente a chamar g com o 7 | parâmetro x e então chamar o f com aquele resultado. 8 | 9 | Em Haskell, composição de funções é praticamente a mesma coisa. Fazemos uma composição de funções 10 | com a função [code].[/code], que é definida como: 11 | 12 | Atente na declaração de tipo. [code]f[/code] deve ter como parâmetro um valor com o mesmo tipo do 13 | valor retornado de [code]g[/code]. Assim, a função resultante assume um parâmetro do mesmo tipo 14 | daquele [code]g[/code] que assume e retorna um valor do mesmo tipo que [code]f[/code] retorna. A 15 | expressão [code]negate . (* 3)[/code] retorna uma função que pega um número, multiplica ele por 3 e 16 | então faz a negação. 17 | 18 | Um dos usos da composição de funções é fazer funções em tempo de execução para passar para outras 19 | funções. Claro, nós podemos usar lambdas para isso, mas muitas vezes, composição de funções é mais 20 | clara e concisa. Digamos que temos uma lista de números e nós queremos então tornar todos em números 21 | negativos. Uma forma para fazer isto seria pegar cada número com valor absoluto e então negá-lo, como isso: 22 | 23 | Observe a lambda e como ela se parece com o resultado da composição da função. Usando composição de 24 | função, podemos reescrever isto como: 25 | 26 | Fabuloso! Composição de funções é associativo à direita, então nós podemos compor muitas funções ao 27 | mesmo tempo. A expressão [code]f (g (z x))[/code] é o equivalente a [code](f . g . z) x[/code]. Com 28 | isso em mente, podemos transformar isto 29 | 30 | nisso 31 | 32 | Mas e quando funções têm muitos parâmetros? Bem, se você quiser usar na composição de funções, 33 | terá que aplicar parcialmente cada função pegando apenas um parâmetro. 34 | [code] sum (replicate 5 (max 6.7 8.9))[/code] pode ser reescrita como 35 | [code](sum . replicate 5 . max 6.7) 8.9[/code] ou como [code]sum . replicate 5 . max 6.7 $ 8.9[/code]. 36 | O que acontece aqui é: uma função que pega o resultado de [code]max 6.7[/code] e aplica em 37 | [code]replicate 5[/code]. Em seguida, a função pega o resultado disso e não a soma do que é criado. 38 | Finalmente, a função é chamada com [code]8.9[/code]. Mas normalmente, você lê isso como: aplique 39 | [code]8.9[/code] em [code]max 6.7[/code], então aplique [code]replicate 5[/code] e depois aplique 40 | [code]sum[/code]. Se você quiser reescrever essa expresão com alguns parênteses usando composição de 41 | funções, você pode começar colocando o último parâmetro na função mais interna após um [code]$[/code] 42 | e depois compor todas as outras chamadas de funções, escrevendo sem o último parâmetro e colocando 43 | pontos entre eles. Se você tiver [code]replicate 100 (product (map (*3) (zipWith max [1,2,3,4,5] 44 | [4,5,6,7,8])))[/code], você pode escrever isso como [code]replicate 100 . product . map (*3) . 45 | zipWith max [1,2,3,4,5] $ [4,5,6,7,8][/code]. Se a função terminar com uma árvore de parânteses, são 46 | grandes as chances de você traduzir em uma composição de função, que terá três operadores de composição. 47 | 48 | Outro uso comum da composição de função é definir funções em um estilo chamado de 'ponto livre'. 49 | Pegue como exemplo esta função que escrevemos anteriormente: 50 | 51 | O [code]xs[/code] é exposto em ambos os lados. Por causa do currying, nós podemos omitir o 52 | [code]xs[/code] em ambos os lados, porque chamar [code]foldl (+) 0[/code] cria uma função que pega uma 53 | lista. Escrevendo a função como [code]sum' = foldl (+) 0[/code] é o que se chama de escrever com o 54 | estilo ponto livre. Como podemos escrever isso no estilo ponto livre? 55 | 56 | Não podemos simplesmente se livrar do [code]x[/code] em ambos os lados. O [code]x[/code] no corpo 57 | da função esta entre parênteses. [code]cos (max 50)[/code] não faz sentido. Você não obtém o cosseno 58 | da função. O que podemos fazer é expressão [code]fn[/code] com uma composição de funções. 59 | 60 | 61 | Excelente! Muitas vezes, o estilo ponto livre é mais legível e conciso, porque ele faz você pensar 62 | sobre as funções e o que cada função que compõe os resultados fazem ao invés de pensar sobre os dados 63 | e como eles são embaralhados. Você pode pegar funções simples e usar composição como cola para tornar 64 | mais complexa. Tanto faz, usar o estilo ponto livre pode se tornar menos legível se a função for muito 65 | complexa. Por isso que não é recomendado fazer longas composições de funções, apesar de eu me declarar 66 | culpado por usar composição muitas vezes. O estilo preferencial é usar uma associação let para 67 | rotular resultados intermediários ou juntar o problema em subproblemas e depois colocá-los juntos para 68 | que a função faça sentido para que alguém que irá ler ao invés de fazer uma longa cadeia de composições. 69 | 70 | Na sessão sobre mapas e filtros, resolvemos um problema encontrando a soma de todos os quadrados 71 | ímpares que são menores do que 10.000. Veja como fica a solução quando colocamos isso na função. 72 | 73 | Sendo um grande fã de composição de função, provavelmente eu teria escrito assim: 74 | 75 | No entanto, se tiver alguma chance de outra pessoa ler esse código, eu escreveria então assim: 76 | 77 | Eu não iria ganhar nenhuma competição de golfe com esse código, mas qualquer um que ler a função 78 | provavelmente vai achar mais fácil ler isso do que uma cadeia de composição. -------------------------------------------------------------------------------- /cap05-recursao/cap05-part03.md: -------------------------------------------------------------------------------- 1 | Um pouco mais de funções recursivas 2 | =================================== 3 | 4 | Agora que já sabemos como pensar recursivamente, vamos implementar algumas funções usando recursão. 5 | Primeiramente, vamos implementar [code]replicate[/code]. [code]replicate[/code] recebe um [code]Int[/code] 6 | junto de algum elemento e retorna uma lista com várias repetições do mesmo elemento. Por exemplo 7 | [code]replicate 3 5[/code] retorna [code][5,5,5][/code]. Pensemos sobre a condição limite. Meu palpite 8 | é de que a condição limite seja 0 ou menos. Se tentarmos replicar alguma coisa zero vezes, devemos 9 | retornar uma lista vazia. O mesmo para números negativos, porque isso não faria sentido. 10 | 11 | Aqui usamos guards em vez de patterns porque estamos interessados em uma condição booleana. Se 12 | [code]n[/code] é menor ou igual a 0, retorna uma lista vazia. Se não, retorna uma lista que tem 13 | [code]x[/code] como primeiro elemento e então [code]x[/code] replicado n-1 vezes como a cauda 14 | (se tratando de listas e algoritmos, a nomenclatura cabeça e cauda é bastante usada, acostume-se). 15 | Eventualmente, a parte [code](n-1)[/code] fará com que a função chegue à nossa condição limite. 16 | 17 | Nota: [code]Num[/code] não é uma subclass de [code]Ord[/code]. Isso significa que o que 18 | constitui um número não necessariamente precisa aderir a uma ordenação. É por isso que temos que 19 | especificar ambas as restrições de classe [code]Num[/code] e [code]Ord[/code] quando fazemos adição 20 | ou subtração e também comparação. 21 | 22 | Em seguida, vamos implementar [code]take[/code], que retorna um certo número de elementos de uma lista. 23 | Por exemplo, [code]take 3 [5,4,3,2,1][/code] retornando [code][5,4,3][/code]. Se tentarmos pegar 0 ou 24 | menos elementos de uma lista, receberemos uma lista vazia. Também, se tentamos pegar algo de uma lista 25 | vazia, receberemos uma lista vazia. Note que essas são as duas condições limite. Então, vamos implementar 26 | a função: 27 | 28 | O primeiro pattern define que se tentarmos pegar 0 ou um número negativo de elementos, vamos receber 29 | a lista vazia. Note que estamos usando [code]_[/code] para comparar com a lista porque não nos 30 | interessamos pelo seu valor nesse caso. Note também que nós usamos um guard, mas sem o 31 | [code]otherwise[/code]. Isso significa que se [code]n[/code] for maior do que 0, o pattern matching irá 32 | para o próximo pattern. O segundo pattern indica que, se tentarmos pegar alguma coisa de uma lista vazia, 33 | vamos receber uma lista vazia. O terceiro pattern quebra a lista em uma cabeça e uma cauda. E então nós 34 | dizemos que pegar [code]n[/code] elementos de uma lista é igual a uma lista que tem [code]x[/code] 35 | como a cabeça e então uma outra lista que pega [code]n-1[/code] elementos da cauda como cauda. Tente usar 36 | um pedaço de papel para escrever como o algoritmo funcionaria em, digamos, 3 de [code][4,3,2,1][/code]. 37 | 38 | [code]reverse[/code] simplesmente inverte uma lista. Pense sobre a condição limite. O que ela seria? 39 | Vamos lá... é a lista vazia! Uma lista vazia invertida é igual à própria lista vazia. Legal. E o resto? 40 | Bem, você poderia dizer que se dividirmos uma lista em cabeça e cauda, a lista invertida é igual à 41 | cauda invertida com a cabeça no final. 42 | 43 | E aí está pronto. 44 | 45 | Como Haskell aceita listas infinitas, nossa recursão não necessariamente precisa ter uma condição 46 | limite. Mas se não tiver, nós ou ficaremos repetindo algo infinitamente ou vamos produzir uma estrutura 47 | de dados infinita, como uma lista infinita. O lado bom das listas infinitas é que nós podemos 48 | cortá-las onde quisermos. [code]repeat[/code] pega um elemento e retorna uma lista infinita que tem 49 | apenas aquele elemento. Uma implementação recursiva disso é realmente fácil. Olhe: 50 | 51 | Chamar [code]repeat 3[/code] nos dará uma lista que começa com [code]3[/code] e então tem uma 52 | quantidade infinita de 3 como cauda. Então chamar [code]repeat 3[/code] resuta em algo como 53 | [code]3:repeat 3[/code], que é [code]3:(3:repeat 3)[/code], que é [code]3:(3:(3:repeat 3))[/code]. 54 | [code]repeat 3[/code] nunca terminará o pattern matching, enquanto [code]take 5 (repeat 3)[/code] 55 | nos retornará uma lista de cinco 3's. Então, essencialmente, é o mesmo que fazer 56 | [code]replicate 5 3[/code]. 57 | 58 | [code]zip[/code] pega duas listas e mescla elas. [code]zip [1,2,3] [2,3][/code] retorna 59 | [code][(1,2),(2,3)][/code], porque ela trunca a lista maior para combinar com o tamanho da lista menor. 60 | E se a gente chamar zip passando uma lista vazia? Bem, vamos receber então, uma lista vazia. 61 | Essa então será a nossa condição limite. Contudo, [code]zip[/code] pega duas listas como parâmetro, 62 | e assim haverá duas condições limite. 63 | 64 | Os primeiros dois patterns dizem que se a primeira ou segunda lista é vazia, nós pegamos uma lista 65 | vazia. O terceiro diz que a chamada de zip em duas listas é igual a parear as suas cabeças e então 66 | continuar a chamar zip nas caudas. A chamada de zip nas listas [code][1,2,3][/code] e 67 | [code]['a','b'][/code] eventualmente tentará chamar zip em [code][3][/code] e [code][][/code]. 68 | Chega-se então ao padrão da condição limite e assim o resultado é [code](1,'a'):(2,'b'):[][/code], 69 | que é exatamente o mesmo que [code][(1,'a'),(2,'b')][/code]. 70 | 71 | Vamos implementar mais uma função da biblioteca padrão — [code]elem[/code]. Ela pega um 72 | elemento e uma lista e vê se aquele elemento está na lista. A condição limite, como na maioria das 73 | vezes com listas, é a lista vazia. Nós sabemos que uma lista vazia não contém elemento algum, então 74 | temos certeza que ela não tem o que estamos procurando. 75 | 76 | Bastante simples e previsível. Se a cabeça não é o elemento, então nós checamos a cauda. Se alcançarmos 77 | uma lista vazia, então o resultado é [code]False[/code]. 78 | 79 | -------------------------------------------------------------------------------- /cap12-punhado-de-monads/cap12-part02.md: -------------------------------------------------------------------------------- 1 | Molhando os nosso pés com Maybe 2 | =============================== 3 | 4 | Agora que nós temos uma vaga ideia sobre o que são monads, vamos ver se podemos tornar essa ideia um pouco menos vaga. 5 | 6 | Para a grande surpresa de ninguém, [code]Maybe[/code] é um monad, então vamos explora-lo um pouco mais e ver se podemos combina-lo com o que nós aprendemos sobre monads. 7 | 8 | Tenha certeza que você entendeu applicatives até aqui. Será bom se você já tiver uma noção de como as várias instâncias de [code]Applicative[/code] trabalham e que tipo de cálculo elas representam, porque monads não são nada mais do que retomar o nosso conhecimento prévio e aprimorar ele. 9 | 10 | Um valor do tipo [code]Maybe a[/code] representa um valor do tipo [code]a[/code] com o contexto de uma possível falha anexada. Um valor de [code]Just "dharma"[/code] significa que a string [code]"dharma"[/code] existe enquanto que o valor de [code]Nothing[/code] representa sua ausência, ou se você olhar para a string como um resultado de um cálculo, isso significa que o cálculo falhou. 11 | 12 | Quando nós olhamos para [code]Maybe[/code] como uma functor, vimos que se iterarmos uma função [code]fmap[/code] sobre ela, ela será mapeada internamente se o valor for [code]Just[/code], caso contrário o [code]Nothing[/code] será mantido porque não há nada para mapear. 13 | 14 | Assim: 15 | 16 | Assim como um applicative functor, que funciona de forma semelhante. Contudo, applicatives também tem a função de embalar as coisas. [code]Maybe[/code] é um applicative functor de tal forma que, quando usamos [code]<*>[/code] para aplicar uma função dentro de um [code]Maybe[/code] para um valor que está dentro de um [code]Maybe[/code], ambos tem que ser [code]Just[/code] para o resultado ser um valor [code]Just[/code], caso contrário o resultado é [code]Nothing[/code]. Faz sentido porque se você tiver perdido a função ou a coisa que você está aplicando, você não poderá fazer nada lá fora com o ar rarefeito, então você terá que propagar uma falha: 17 | 18 | Quando usamos o estilo applicative para ter funções normais agindo em valores [code]Maybe[/code] isto é semelhante. Todos os valores têm que ser [code]Just[/code], caso contrário todos são [code]Nothing[/code]! 19 | 20 | E agora vamos pensar como faríamos [code]>>=[/code] para [code]Maybe[/code]. Como nós falamos, [code]>>=[/code] tem um valor monadico, e uma função que tem um valor normal e retorna um valor monadico e consegue aplicar essa função para um valor monadico. Como ele faz isso, se a função tem um valor normal? Bem, para fazer isso, ele tem que levar em conta o contexto desse valor monadico. 21 | 22 | Neste caso, [code]>>=[/code] levaria um valor [code]Maybe a[/code] e uma função do tipo [code]a -> Maybe b[/code] que de alguma maneira aplica a função para o [code]Maybe a[/code]. Para descobrir como ele faz isso, podemos usar a intuição que temos de [code]Maybe[/code] ser um applicative functor. Vamos dizer que temos uma função [code]\x -> Just (x+1)[/code]. Ela pega um número, acrescenta [code]1[/code] a ele e o embala com um [code]Just[/code]: 23 | 24 | Se alimentarmos ela com [code]1[/code], ela será calculada como [code]Just 2[/code]. Se nós dermos a ela o número [code]100[/code], o resultado será [code]Just 101[/code]. Muito simples. Agora aqui vai o chute: Como nós alimentamos um valor [code]Maybe[/code] para esta função? Se nós pensarmos sobre como [code]Maybe[/code] atua como um applicative functor, responder isso será muito fácil. Se alimentarmos isso com um valor [code]Just[/code], pegarmos o que está dentro de [code]Just[/code] e aplicarmos a função nele. Se der a ele um [code]Nothing[/code], hmm, bem, então estaremos com uma função porém [code]Nothing[/code] (nada) para aplicar nela. Neste caso vamos apenas fazer o que nós fizemos antes de dizer que o resultado é [code]Nothing[/code]. 25 | 26 | Ao invés de chamar isso de [code]>>=[/code], iremos chama-lo de [code]applyMaybe[/code] por enquanto. Ela irá pegar um [code]Maybe a[/code] e uma função que retorna um [code]Maybe b[/code] e manipula-los para aplicar esta função ao [code]Maybe a[/code]. Aqui está o código: 27 | 28 | Ok, agora vamos jogar com ele um pouco. Vamos usa-lo como uma função infixa com o valor de [code]Maybe[/code] no lado esquerdo e a função no lado direto: 29 | 30 | No exemplo acima, nós vimos que quando usamos [code]applyMaybe[/code] com um valor [code]Just[/code] e uma função, a função simplesmente será aplicada no valor dentro de [code]Just[/code]. Quando tentarmos usar isso com um [code]Nothing[/code], o resultado completo será [code]Nothing[/code]. Que tal se a função retornar apenas um [code]Nothing[/code]? Vamos ver: 31 | 32 | Exatamente o que nós esperávamos. Se o valor monadico à esquerda é um [code]Nothing[/code], a coisa toda é [code]Nothing[/code]. E se a função a direita retorna um [code]Nothing[/code], o resultado novamente é [code]Nothing[/code]. Isto é muito similar quando nós usamos [code]Maybe[/code] como um applicative e nós obtemos um resultado [code]Nothing[/code] se em alguma parte havia um [code]Nothing[/code]. 33 | 34 | Parece que para [code]Maybe[/code], nós temos que descobrir como tiramos um valor imaginário e alimentamos ele com uma função que recebe um valor normal e retorna um imaginário. Nós fazemos isso mantendo em mente que um valor [code]Maybe[/code] representa um cálculo que pode ter falhado. 35 | 36 | Você pode estar se perguntado, como é que isso pode ser útil? Pode parecer que applicative functors são mais fortes que monads, quando applicative functors nos permitem ter uma função normal para faze-la funcionar operando em valores com contextos. Vamos ver que monads podem fazer isso também, porque eles são uma evolução de applicative functors, e que eles também podem fazer alguma coisas legais que applicative functors não podem. 37 | 38 | Iremos voltar para [code]Maybe[/code] em um minuto, mas primeiro, vamos verificar que tipo de classe pertence a monads. -------------------------------------------------------------------------------- /cap07-modulos/cap07-part03.md: -------------------------------------------------------------------------------- 1 | Data.Char 2 | ========= 3 | 4 | O módulo [code]Data.Char[/code] é exatamente o que o seu nome sugere. Exporta funções que lidam 5 | com caracteres. É extremamente útil para filtrar ou mapear strings, que nada mais são do que listas de 6 | caracteres. 7 | 8 | [code]Data.Char[/code] também exporta vários predicados de caracteres. Isto é, funções que recebem um 9 | caracter e nos diz se uma proposição é verdadeira ou falsa. Alguns exemplos: 10 | 11 | [function]isControl[/function] testa se um caracter é um caracter de controle. 12 | 13 | [function]isSpace[/function] testa se um caracter é um espaço em branco, o que inclui espaços, tabs, 14 | novas linhas, etc. 15 | 16 | [function]isLower[/function] testa se um caracter é minúsculo. 17 | 18 | [function]isUpper[/function] testa se um caracter é maiúsculo. 19 | 20 | [function]isAlpha[/function] testa se um caracter é uma letra. 21 | 22 | [function]isAlphaNum[/function] testa se um caracter é uma letra ou um número. 23 | 24 | [function]isPrint[/function] testa se um caracter é imprimível. Caracteres de controle, por exemplo, 25 | não são. 26 | 27 | [function]isDigit[/function] testa se um caracter é um dígito. 28 | 29 | [function]isOctDigit[/function] testa se um caracter é um dígito octal. 30 | 31 | [function]isHexDigit[/function] testa se um caracter é um dígito hexadecimal. 32 | 33 | [function]isLetter[/function] testa se um caracter é uma letra. 34 | 35 | [function]isMark[/function] testa se um caracter é um caracter de marcação Unicode, que quando 36 | combinados com letras precedentes, formam letras com acentos. Use se for francês. 37 | 38 | [function]isNumber[/function] testa se um caracter é numérico. 39 | 40 | [function]isPunctuation[/function] testa se um caracter é uma pontuação. 41 | 42 | [function]isSymbol[/function] testa se um caracter é matemático ou um símbolo de moedas. 43 | 44 | [function]isSeparator[/function] testa se um caracter é espaço ou separador Unicode. 45 | 46 | [function]isAscii[/function] testa se um caracter é um dos primeiros 128 Unicode. 47 | 48 | [function]isLatin1[/function] testa se um caracter é um dos primeiros 256 Unicode. 49 | 50 | [function]isAsciiUpper[/function] testa se um caracter é ASCII e maiúsculo. 51 | 52 | [function]isAsciiLower[/function] testa se um caracter é ASCII e minúsculo. 53 | 54 | Todos esses predicados têm tipos [code]Char -> Bool[/code]. Na maior parte das vezes eles serão 55 | usados para filtrar algo de strings. Por exemplo, digamos que estamos fazendo um programa com login 56 | que necessita que o usuário seja composto apenas por caracteres alfanuméricos. Poderíamos usar a função 57 | [code]all[/code] do [code]Data.List[/code] em combinção com predicados do [code]Data.Char[/code] 58 | para testar sua validade. 59 | 60 | 61 | 62 | 63 | Que bacana! Caso não se lembre, [code]all[/code] recebe um predicado e uma lista e retorna 64 | [code]True[/code] apenas se o predicado aprova todos os elementos. 65 | 66 | Podemos ainda usar a [code]isSpace[/code] para simular o funcionamento da função [code]words[/code] 67 | do [code]Data.List[/code]. 68 | 69 | 70 | Hmmm, muito parecido mesmo, mas manteve os espaços em branco. O que poderíamos fazer? Eu sei, pelo 71 | menos. Vamos filtrar esse pessoal. 72 | 73 | 74 | Ah. 75 | 76 | A [code]Data.Char[/code] ainda exporta o tipo de dados [code]Ordering[/code]. O [code]Ordering[/code] 77 | pode ter valor [code]LT[/code], [code]EQ[/code] ou [code]GT[/code]. É como uma enumeração. Descreve os 78 | possíveis resultados ao comparar dois elementos. O tipo [code]GeneralCategory[/code] também é uma 79 | enumeração. Tem algumas possíveis categorias que um caracter pode se encaixar. A principal função de 80 | conseguir a categoria geral de caracteres é [code]generalCategory[/code]. Tem tipo 81 | [code]generalCategory :: Char -> GeneralCategory[/code]. Existem ainda outras 31 categorias que não 82 | listaremos aqui, mas vamos dar uma olhada na função. 83 | 84 | 85 | Já que o tipo [code]GeneralCategory[/code] é parte da typeclass [code]Eq[/code], poderíamos testar algo 86 | como [code]generalCategory c == Space[/code]. 87 | 88 | [function]toUpper[/function] converte um caracter para maiúsculo. Espaços, números e outros permanecem 89 | iguais. 90 | 91 | [function]toLower[/function] converte um caracter para minúsculo. 92 | 93 | [function]toTitle[/function] converte um caracter para title-case. Para a maioria dos caracteres, 94 | titlecase é o mesmo que maiúsculas. 95 | 96 | [function]digitToInt[/function] converte um caracter para [code]Int[/code]. Para que funcione, 97 | o caracter deve estar nas ranges [code]'0'..'9'[/code], [code]'a'..'f'[/code] ou [code]'A'..'F'[/code]. 98 | 99 | 100 | [function]intToDigit[/function] é o inverso da função [code]digitToInt[/code]. Recebe um 101 | [code]Int[/code] na range [code]0..15[/code] e converte para um caracter minúsculo. 102 | 103 | 104 | As funções [function]ord[/function] e [code]chr[/code] convertem caracteres para seus correspondentes 105 | em números e vice-versa: 106 | 107 | 108 | Os valores [code]ord[/code] de dois caracteres é referente à tabela Unicode. 109 | O Código de César é um método primitivo 110 | de codificar mensagens ao associar caracteres por um correspondente (seguindo a mesma sequência). 111 | Nós mesmos poderíamos construir nosso código criptografado sem precisar debruçar sobre um alfabeto. 112 | 113 | 114 | Aqui, primeiro convertemos nossa string para uma lista de números. Então aumentamos ou diminuimos 115 | uma certa quantidade de inteiros de toda a lista e convertemos de volta para caracteres. Se você já 116 | está craque na criação de funções, já escreveu [code]map (chr . (+ shift) . ord) msg[/code]. Vamos 117 | tentar criptografar algumas mensagens. 118 | 119 | 120 | Parece que está certo. Para decodificar a mensagem é só necessário voltar o número de posições em todos 121 | caracteres, trazendo-os à posição inicial. 122 | -------------------------------------------------------------------------------- /cap06-funcoes-alta-ordem/cap06-part02.md: -------------------------------------------------------------------------------- 1 | Alguma altissima-ordem está em ordem 2 | ==================================== 3 | 4 | Funções podem receber funções como parâmetros e também retornar funções. Para ilustrar isto, 5 | nós vamos fazer uma função que recebe uma função e aplica ela duas vezes em algo! 6 | 7 | Antes de mais nada, repare na declaração de tipo. Antes, nós não precisavamos de parênteses porque 8 | [code]->[/code] é naturalmente associativa à direita. Entretando, aqui, eles são obrigatórios. 9 | Eles indicam que o primeiro parâmetro é uma função que recebe algo e retorna esta mesma coisa. 10 | O segundo parâmetro é algo deste tipo também e o valor de retorno é também do mesmo tipo. 11 | Nós também poderíamos ler esta declaração de uma forma mais curried, mas para nos poupar 12 | desta dor de cabeça, nós vamos apenas dizer que esta função recebe dois parâmetros e retorna uma coisa. 13 | O primeiro parâmetro é uma função (do tipo [code]a -> a[/code]) e o segundo é o próprio [code]a[/code]. 14 | A função pode ser também [code]Int -> Int[/code] ou [code]String -> String[/code] ou qualquer outra 15 | coisa. Neste caso, o segundo parâmetro também deverá ser deste tipo. 16 | 17 | Nota: De agora em diante, nós diremos que funções recebem vários argumentos, apesar do fato de que cada 18 | função recebe apenas um parâmetro e retorna uma função parcialmente aplicada até chegarmos à uma função 19 | que retorna um valor sólido. Então, em nome da simplicidade, diremos que [code]a -> a -> a[/code] recebe 20 | dois parâmetros, apesar de sabermos o que realmente está ocorrendo por debaixo dos panos. 21 | 22 | O corpo da função é bem simples. Apenas usamos o parâmetro [code]f[/code] como uma função, aplicando 23 | [code]x[/code] à ela ao separá-los com um espaço e então aplicando o resultado a [code]f[/code] novamente. 24 | De qualque modo, brincando com a função: 25 | 26 | Fica evidente o quão legal e útil é a aplicação parcial. Se a nossa função requer que passemos à ela uma 27 | função que recebe apenas um parâmetro, nós podemos apenas aplicar parcialmente uma função no ponto onde 28 | se recebe apenas um parâmetro e então passá-lo. 29 | 30 | Agora nós vamos usar programação de alta ordem para implementar uma função realmente útil que está na 31 | biblioteca padrão. Ela se chama [code]zipWith[/code]. Ela recebe uma função e duas listas como 32 | parâmetros e então junta as duas listas aplicando a função entre os elementos correspondentes. 33 | Aqui está como nós vamos implementá-la: 34 | 35 | Olhe a declaração do tipo. O primeiro parâmetro é uma função que recebe duas coisas e produz uma 36 | terceira. Elas não precisam ser do mesmo tipo, mas podem. O segundo e o terceiro parâmetros são listas. 37 | O resultado é também uma lista. O primeiro tem que ser uma lista de [code]a[/code]s, porque a função 38 | de junção recebe [code]a[/code]s como primeiros argumentos. A segunda tem que ser uma lista de 39 | [code]b[/code]s, porque o segundo argumento da função é do tipo [code]b[/code]. O resultado é uma 40 | lista de [code]c[/code]s. Se a declaração do tipo de uma função diz que ela aceita uma função 41 | [code]a -> b -> c[/code] como parâmetro, ela também aceitará uma função 42 | [code]a -> a -> a[/code], mas não o contrário! Lembre-se de que, quando você está fazendo funções, 43 | especialmente as de alta ordem, e você não está seguro sobre o tipo, você pode simplesmente tentar 44 | omitir a declaração do tipo e então verificar o que o Haskell infere que seja, usando [code]:t[/code]. 45 | 46 | A ação na função é bem parecida com a [code]zip[/code] normal. As condições limite são as mesmas, 47 | mas há um argumento extra, qual seja, a função de junção, mas este argumento não importa nas condições 48 | limite, então usamos um [code]_[/code] para ele. E o corpo da função no último pattern é também 49 | parecido com [code]zip[/code], mas ele não faz [code](x,y)[/code], e sim [code]f x y[/code]. Uma única 50 | função de alta ordem pode ser usada para uma enorme variedade de tarefas se é suficientemente geral. 51 | Aqui está uma pequena demonstração de todas as diferentes coisas que a nossa função [code]zipWith'[/code] 52 | é capaz de fazer: 53 | 54 | Como você pode ver, uma única função de alta ordem pode ser usada de modos muito versáteis. A programação 55 | imperativa utiliza normalmente coisas como iterações com for, iterações com while, setar 56 | algo à uma variável, verificar seu estado, e etc., para atingir um certo comportamento e então envolve 57 | isso em uma interface, como em uma função. Já a programação funcional, utiliza funções de alta ordem 58 | para abstrair padrões comuns, como examinar duas listas aos pares e fazer algo nestes pares ou receber 59 | um conjunto de soluções e eliminar as que você não precisa. 60 | 61 | Nós vamos implementar outra função que também já está na biblioteca padrão, chamada [code]flip[/code]. 62 | Flip simplesmente recebe uma função e retorna uma função que é parecida com a função original mas 63 | cujos primeiros dois argumentos são invertidos. Nós podemos implementá-la assim: 64 | 65 | Lendo a declaração do tipo, nós dizemos que ela recebe uma função que recebe um [code]a[/code] e um 66 | [code]b[/code] e retorna uma função que recebe um [code]b[/code] e um [code]a[/code]. Mas justamente 67 | porque funções são curried por padrão, o segundo par de parênteses é na verdade desnecessário, 68 | porque [code]->[/code] é associativo à direita por padrão. 69 | [code](a -> b -> c) -> (b -> a -> c)[/code] é o mesmo que 70 | [code](a -> b -> c) -> (b -> (a -> c))[/code], e que é o mesmo que 71 | [code](a -> b -> c) -> b -> a -> c[/code]. Nós escrevemos que 72 | [code]g x y = f y x[/code]. Se isso é verdade, então [code]f y x = g x y[/code] também deve ser, certo? 73 | Tendo isso em mente, nós podemos definir esta função de uma forma ainda mais simples. 74 | 75 | Aqui, nós tiramos vantagem do fato de que funções são curried. Quando nós chamamos 76 | [code]flip' f[/code] sem os parâmetros [code]x[/code] e [code]y[/code], ela vai retornar uma 77 | [code]f[/code] que recebe esses dois parâmetros, mas que os chama invertidos. Mesmo que funções 78 | invertidas sejam normalmente passadas para outras funções, nós tiramos vantagem do currying 79 | quando fazemos funções de alta ordem pensando adiante e escrevendo como o seu resultado final seria 80 | se ela fosse chamada totalmente aplicada. -------------------------------------------------------------------------------- /cap02-comecando/cap02-part01.md: -------------------------------------------------------------------------------- 1 | Começando 2 | ========= 3 | 4 | ** Preparar, apontar, foi! ** 5 | 6 | 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 nela explico 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 [code]ghci[/code]. Você será agraciado com algo parecido com isto: 7 | 8 | Meus parabéns, você esta no GHCi! O prompt aqui é [code]Prelude>[/code] no entando como isto é demasiadamente longo quando você carrega algumas coisas na sua sessão, nós iremos usar [code]ghci>[/code]. Se você quer ter o mesmo prompt, digite [code]:set prompt "ghci> "[/code]. 9 | 10 | 11 | Veja algumas simples operações aritméticas. 12 | 13 | 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. 14 | 15 | 16 | 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. Fazendo [code]5 * -3[/code] o GHCi irá gritar com você, porém fazendo [code]5 * (-3)[/code] irá funcionar perfeitamente. 17 | 18 | Álgebra booleana também é bastante simples. Você provavelmente já sabe que [code]&&[/code] refere-se a expressão booleana and, [code]||[/code] refere-se a expressão booleana or. [code]not[/code] faz a negação de um [code]True[/code] ou de um [code]False[/code]. 19 | 20 | Testando para igualdades fica algo assim. 21 | 22 | Que tal se fizermos [code]5 + "llama"[/code] ou [code]5 == True[/code]? Bem, se nós tentarmos o primeiro trecho destes código, nós teremos uma grande e assustadora mensagem de erro! 23 | 24 | Credo! O que o GHCi esta dizendo para nós aqui é que [code]"llama"[/code] não é um número e que ele não sabe como adicionar isto ao número 5. Se isso não fosse um [code]"llama"[/code] mas um [code]"four"[/code] ou um [code]"4"[/code], Haskell ainda assim não iria considerá-lo como um número. [code]+[/code] espera-se que tenha a sua esquerda e a sua direita um número. 25 | Se nós tentarmos fazer [code]True == 5[/code], o GHCi irá nos dizer que os tipos são diferentes. Visto que o [code]+[/code] funciona somente em coisas consideradas números, o [code]==[/code] 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. 26 | 27 | Nota: Você pode fazer [code]5 + 4.0[/code] porque [code]5[/code] é adaptável e pode se comportar como um inteiro ou um ponto flutuante (floating-point number). [code]4.0[/code] não pode se comportar como um inteiro, então o [code]5[/code] é o único que pode ser adaptado. 28 | 29 | Talvez você não saiba mas nós estamos usando função agora o tempo todo. Por exemplo, [code]*[/code] é 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. 30 | 31 | 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. 32 | 33 | 34 | A função [code]succ[/code] 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 [code]min[/code] e [code]max[/code] 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: 35 | 36 | 37 | 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. 38 | 39 | No entanto, se nós quisermos ter o sucessor do produto dos números 9 e 10, nós não podemos escrever [code]succ 9 * 10[/code] 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 [code]succ (9 * 10)[/code] para obter 91. 40 | 41 | 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 [code]div[/code] recebe dois inteiros e realiza a divisão deles. Fazendo [code]div 92 10[/code] 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 [code]92 `div` 10[/code] e logicamente isto ficará muito mais claro. 42 | 43 | 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 [code]foo()[/code], [code]bar(1)[/code] ou [code]baz(3, "haha")[/code]. Como dissemos, espaços são utilizados para a aplicação de funções em Haskell. Então estas funções em Haskell deveriam ser [code]foo[/code], [code]bar 1[/code] e [code]baz 3 "haha"[/code]. Se você enxergar algo como [code]bar (bar 3)[/code] não significa que aquela chamada de [code]bar[/code] recebe [code]bar[/code] e [code]3[/code] como parâmetros. Isto significa que nós primeiros chamamos a função [code]bar[/code] com o parâmetro [code]3[/code] para obter algum número e então chamar [code]bar[/code] novamente com este número. Em C, isso seria algo como [code]bar(bar(3))[/code]. -------------------------------------------------------------------------------- /cap02-comecando/cap02-part03.md: -------------------------------------------------------------------------------- 1 | Uma introdução às listas 2 | ======================== 3 | 4 | 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. 5 | 6 | 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! 7 | 8 | Nota: Nós podemos utilizar a palavra-chave [code]let[/code] para definir o nome correto no GHCi. Fazer [code]let a = 1[/code] dentro do GHCi é o equivalente a escrever [code]a = 1[/code] em um script e carregá-lo. 9 | 10 | 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 [code][1,2,'a',3,'b','c',4][/code], 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. [code]"hello"[/code] é só um açúcar sintático para [code]['h','e','l','l','o'][/code]. Como strings são listas, nós podemos utilizar funções de listas nelas, o que é realmente útil. 11 | 12 | Uma prática comum é colocar duas listas juntas. Fazemos isso utilizando o operador [code]++[/code]. 13 | 14 | Veremos repetidamente o uso do operador [code]++[/code] em strings grandes. Quando juntamos duas listas (mesmo se você adicionar uma lista de um elemento só em outra lista, como por exemplo:[code][1,2,3] ++ [4][/code]), internamente o Haskell irá percorrer toda a lista que esta do lado esquerdo do [code]++[/code]. 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 [code]:[/code] (também chamado de contra operador) será instantâneo. 15 | 16 | Observe que o [code]:[/code] recebe um número e uma lista de números ou um caractere e uma lista de caracteres, enquanto o [code]++[/code] recebe duas listas. Mesmo que você esteja adicionando um elemento ao final de uma lista com [code]++[/code], você terá que demarcá-la com colchetes para que se torne lista. 17 | 18 | [code][1,2,3][/code] é na verdade açucar sintático para [code]1:2:3:[][/code]. [code][][/code] é uma lista vazia. Se acrescentarmos [code]3[/code] nele, ele irá se tornar [code][3][/code]. Se acrescentarmos [code]2[/code], se tornará [code][2,3][/code] e assim por diante. 19 | 20 | Nota: [code][][/code], [code][[]][/code] e [code][[],[],[]][/code] 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. 21 | 22 | Se você deseja obter um elemento de uma lista pelo seu índice, utilize [code]!![/code]. O índice inicia a partir de 0. 23 | 24 | Mas se você tentar obter o sexto elemento de uma lista que contém somente quatro elementos, você obterá um erro portanto seja cuidadoso! 25 | 26 | Listas também podem conter listas. Elas também podem conter listas que contêm listas que contêm listas... 27 | 28 | 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. 29 | 30 | As listas poderão ser comparadas se as coisas que elas contêm puderem ser comparadas. Quando utilizamos [code]<[/code], [code]<=[/code], [code]>[/code] e [code]>=[/code] 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. 31 | 32 | O que mais você pode fazer com listas? Aqui estão algumas funções básicas para operações em listas. 33 | 34 | [code]head[/code] recebe uma lista e retorna o seu head. O head (cabeça) de uma lista é basicamente o primeiro elemento. 35 | 36 | [code]tail[/code] recebe uma lista e retorna a sua "cauda". Em outras palavras, ele decepa a pobre cabeça de uma lista sobrando apenas a cauda. 37 | 38 | [code]last[/code] recebe uma lista e retorna o seu último elemento. 39 | 40 | [code]init[/code] recebe uma lista e retorna tudo com exceção do último elemento. 41 | 42 | Se pensarmos em uma lista como um monstro, aqui está o que é o que. 43 | 44 | Mas o que acontece se tentarmos obter o head de uma lista vazia? 45 | 46 | 47 | WOW! Tudo explode bem na nossa cara! Isso não é um mostro, não tem cabeça! Quando for utilizar [code]head[/code], [code]tail[/code], [code]last[/code] e [code]init[/code], 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. 48 | 49 | [code]length[/code] recebe uma lista e retorna o seu length (tamanho), obviamente. 50 | 51 | [code]null[/code] verifica se a lista é vazia. Se for, então retorna [code]True[/code], senão retorna [code]False[/code]. Utilize esta função no lugar de [code]xs == [][/code] (Caso você tiver uma lista chamada [code]xs[/code]) 52 | 53 | [code]reverse[/code] reverte uma lista. 54 | 55 | [code]take[/code] recebe um número e uma lista. Ele extrai a quantidade de elementos desde o início da lista. Observe. 56 | 57 | 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. 58 | 59 | [code]drop[/code] funciona de forma similar, só que retira o número de elementos a partir do ínicio da lista. 60 | 61 | [code]maximum[/code] recebe uma lista de coisas que podem ser colocadas em algum tipo de ordem e retorna o seu maior elemento. 62 | 63 | [code]minimum[/code] retorna o menor. 64 | 65 | [code]sum[/code] recebe uma lista de números e retorna a sua soma. 66 | 67 | [code]product[/code] recebe uma lista de números e retorna o seu produto. 68 | 69 | [code]elem[/code] 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. 70 | 71 | Estas são algumas funções básicas para operações em listas. Iremos dar mais uma olhada em funções de listas depois. -------------------------------------------------------------------------------- /cap06-funcoes-alta-ordem/cap06-part01.yml: -------------------------------------------------------------------------------- 1 | Funções de alta ordem 2 | ===================== 3 | 4 | Funções em Haskell podem retornar ou receber outras funções como parâmetros. Uma função que realiza 5 | alguma dessas coisas é chamada de função de alta ordem. Funções de alta ordem não são apenas parte 6 | integrante do Haskell, mas praticamente o prório Haskell. Quando você quer criar cálculos 7 | definindo o que as coisas são ao invés de definir passos que mudam algum estado e talvez fazer uma 8 | iteração, funções de alta ordem são indispensáveis. Elas são uma forma muito poderosa de resolver 9 | problemas e pensar em programas. 10 | 11 | ** Funções curried ** 12 | 13 | Toda função em Haskell oficialmente recebe apenas um parâmetro. Então, como foi possível quando nós 14 | definimos e usamos várias funções que tinham mais de um parâmetro até aqui? Bom, com um truque muito 15 | esperto! Todas as funções que aceitaram vários parâmetros até aqui eram funções curried. 16 | O que isso significa? Você vai entender melhor com um exemplo. Peguemos nossa boa amiga, a função 17 | [code]max[/code]. Parece que ela recebe dois parâmetros e retorna o que é maior. Executar 18 | [code]max 4 5[/code] cria primeiro uma função que recebe um parâmetro e retorna ou 19 | [code]4[/code] ou este parâmetro, dependendo de qual é o maior. Então, [code]5[/code] é aplicado à 20 | esta função e ela produz o resultado desejado. Isso parece um bocado, e é de fato, um conceito muito 21 | interessante. As duas chamadas a seguir são equivalentes: 22 | 23 | Colocar um espaço entre duas coisa é simplesmente aplicar a função. O espaço é como se um fosse 24 | operador e ele tem máxima precedência. Examinemos o tipo de [code]max[/code]. Ele é 25 | [code]max :: (Ord a) => a -> a -> a[/code]. Isso também pode ser escrito como 26 | [code]max :: (Ord a) => a -> (a -> a)[/code]. Isso poderia ser lido assim: 27 | [code]max[/code] recece um [code]a[/code] e retorna (com aquele [code]->[/code]) uma função que 28 | recebe um [code]a[/code] e retorna um [code]a[/code]. É por isso que o tipo do retorno e os parâmetros 29 | das funções são simplesmente separados por setas. 30 | 31 | Então como isso pode nos beneficiar? Colocando de forma simples, se nós chamamos uma função com 32 | parâmetros faltando, recebemos uma função parcialmente aplicada, ou seja, uma função que 33 | recebe o número de parâmetros que omitimos. Usar aplicação parcial (chamar uma função faltando parâmetros, 34 | se você preferir) é uma maneira de criar funções em tempo real para passá-la a outra função ou 35 | alimentá-la com dados. 36 | 37 | Dê uma olhada nesta função ridículamente simples: 38 | 39 | O que realmente acontece quando executamos [code]multThree 3 5 9[/code] ou [code]((multThree 3) 5) 9[/code]? 40 | Primeiro, [code]3[/code] é aplicado à [code]multThree[/code], porque eles estão separados por um espaço. 41 | Isso cria uma função que recebe um parâmetro e retorna uma função. Então [code]5[/code] é alocado à esta, 42 | que cria uma função que receberá um parâmetro e multiplicá-lo por 15. [code]9[/code] é passado à esta 43 | função e o resultado é 135 ou algo assim. Lembre-se que o tipo desta função também poderia ser escrito 44 | como [code]multThree :: (Num a) => a -> (a -> (a -> a))[/code]. O negócio antes de 45 | [code]->[/code] é o parâmetro que a função recebe e a que vêm depois é o que ela retorna. Sendo assim, 46 | nossa função recebe um [code]a[/code] e retorna a função do tipo [code](Num a) => a -> (a -> a)[/code]. 47 | Da mesma forma, esta função recebe um [code]a[/code] e retorna uma função do tipo [code](Num a) => a -> a[/code]. 48 | E esta função, finalmente, recebe apenas um [code]a[/code] e retorna um [code]a[/code]. Veja isto: 49 | 50 | Ao chamar funções com parâmetros faltando, por assim dizer, estamos criando funções em tempo real. 51 | E se nós quiséssemos criar uma função que recebe um número e o compara com [code]100[/code]? Poderíamos 52 | fazer algo assim: 53 | 54 | Se chamamos esta função com [code]99[/code], ela retorna [code]GT[/code]. Barbada. Repare que o 55 | [code]x[/code] está à direita em ambos os lados da equação. Agora, pensemos sobre o que 56 | [code]compare 100[/code] retorna. Ela retorna uma função que recebe um número e o compara com 57 | [code]100[/code]. Uau! Não é esta a função que nós queríamos? Podemos escrever isto assim: 58 | 59 | A declaração do tipo permanece idêntica, porque [code]compare 100[/code] retorna uma função. 60 | Compare tem o tipo [code](Ord a) => a -> (a -> Ordering)[/code] e chamando-a com 61 | [code]100[/code] retorna um [code](Num a, Ord a) => a -> Ordering[/code]. A restrição de classe 62 | adicional aparece sorrateiramente porque [code]100[/code] também é parte do typeclass 63 | [code]Num[/code]. 64 | 65 | Ei! Tenha certeza que realmente entendeu como é que funcionam as funções curried e a 66 | aplicação parcial porque elas são muito importantes! 67 | 68 | Funções infix também podem ser parcialmente aplicadas usando secções. Para secionar uma função 69 | infix, simplesmente coloque ela entre parênteses e passe apenas um parâmetro. Isso cria uma 70 | função que recebe um parâmetro e então aplica ele no lado em que está faltando o operando. 71 | Uma função absurdamente trivial: 72 | 73 | Executando, digamos, [code]divideByTen 200[/code] é o equivalente a fazer [code]200 / 10[/code], bem 74 | como [code](/10) 200[/code]. Uma função que verifica se o caracter passado a ela é uma letra maiúscula: 75 | 76 | A única coisa especial sobre secções é o uso do [code]-[/code]. Pela definição de secções, 77 | [code](-4)[/code] resultará em uma função que recebe um número e subtrai 4 dele. Entretanto, por 78 | conveniência, [code](-4)[/code] significa menos quatro. Logo, se você quer fazer uma função que subtrai 79 | 4 de um número que ela recebe como parâmetro, aplique parcialmente a função [code]subtract[/code] como 80 | neste exemplo: [code](subtract 4)[/code]. 81 | 82 | O que acontece se nós tentarmos executar [code]multThree 3 4[/code] no GHCI ao invés de associar isto 83 | à um nome com um let ou passando à uma outra função? 84 | 85 | O GHCI está nos dizendo que esta expressão produziu uma função do tipo [code]a -> a[/code] mas que 86 | ele não sabe como apresentar ela na tela. Funções não são instâncias do typeclass 87 | [code]Show[/code], então não temos como ter uma representação legal da string de uma função. 88 | 89 | Quando rodamos, digamos, [code]1 + 1[/code] no prompt do GHCI, ele primeiro calcula isso como 90 | [code]2[/code] e então chama [code]show[/code] em [code]2[/code] para receber uma representação 91 | em forma de texto deste número. E a representação textual de [code]2[/code] é justamente a string 92 | [code]"2"[/code], que é então apresentada na tela. -------------------------------------------------------------------------------- /cap14-zippers/cap14-part05.md: -------------------------------------------------------------------------------- 1 | A very simple file system 2 | ========================= 3 | 4 | Now that we know how zippers work, let's use trees to represent a very simplefile system and then make a zipper for that file system, which will allow us tomove between folders, just like we usually do when jumping around our filesystem. 5 | 6 | If we take a simplistic view of the average hierarchical file system, we seethat it's mostly made up of files and folders. Files are units of data and comewith a name, whereas folders are used to organize those files and can containfiles or other folders. So let's say that an item in a file system is either afile, which comes with a name and some data, or a folder, which has a name andthen a bunch of items that are either files or folders themselves. Here's a datatype for this and some type synonyms so we know what's what: 7 | 8 | A file comes with two strings, which represent its name and the data it holds. Afolder comes with a string that is its name and a list of items. If that list isempty, then we have an empty folder. 9 | 10 | Here's a folder with some files and sub-folders: 11 | 12 | That's actually what my disk contains right now. 13 | 14 | - A zipper for our file system 15 | 16 | Now that we have a file system, all we need is a zipper so we can zip and zoomaround it and add, modify and remove files as well as folders. Like with binarytrees and lists, we're going to be leaving breadcrumbs that contain info aboutall the stuff that we chose not to visit. Like we said, a single breadcrumbshould be kind of like a node, only it should contain everything except thesub-tree that we're currently focusing on. It should also note where the holeis so that once we move back up, we can plug our previous focus into the hole. 17 | 18 | In this case, a breadcrumb should be like a folder, only it should be missingthe folder that we currently chose. Why not like a file, you ask? Well, becauseonce we're focusing on a file, we can't move deeper into the file system, so itdoesn't make sense to leave a breadcrumb that says that we came from a file. Afile is sort of like an empty tree. 19 | 20 | If we're focusing on the folder [code]"root"[/code] and we thenfocus on the file [code]"dijon_poupon.doc"[/code], what should thebreadcrumb that we leave look like? Well, it should contain the name of itsparent folder along with the items that come before the file that we're focusingon and the items that come after it. So all we need is a [code]Name[/code] and two lists of items. By keeping separate lists forthe items that come before the item that we're focusing and for the items thatcome after it, we know exactly where to place it once we move back up. So thisway, we know where the hole is. 21 | 22 | Here's our breadcrumb type for the file system: 23 | 24 | And here's a type synonym for our zipper: 25 | 26 | Going back up in the hierarchy is very simple. We just take the latestbreadcrumb and assemble a new focus from the current focus and breadcrumb.Like so: 27 | 28 | Because our breadcrumb knew what the parent folder's name was, as well as theitems that came before our focused item in the folder (that's [code]ls[/code]) and the ones that came after (that's [code]rs[/code]), moving up was easy. 29 | 30 | How about going deeper into the file system? If we're in the[code]"root"[/code] and we want to focus on[code]"dijon_poupon.doc"[/code], the breadcrumb that we leave is going toinclude the name [code]"root"[/code] along with the items thatprecede [code]"dijon_poupon.doc"[/code] and the ones that comeafter it. 31 | 32 | Here's a function that, given a name, focuses on a file of folder that's locatedin the current focused folder: 33 | 34 | [code]fsTo[/code] takes a [code]Name[/code] and a[code]FSZipper[/code] and returns a new [code]FSZipper[/code] that focuses on the filewith the given name. That file has to be in the current focused folder. Thisfunction doesn't search all over the place, it just looks at the current folder. 35 | 36 | First we use [code]break[/code] to break the list of items in afolder into those that precede the file that we're searching for and those thatcome after it. If you remember, [code]break[/code] takes apredicate and a list and returns a pair of lists. The first list in the pairholds items for which the predicate returns [code]False[/code].Then, once the predicate returns [code]True[/code] for an item, itplaces that item and the rest of the list in the second item of the pair. Wemade an auxilliary function called [code]nameIs[/code] that takesa name and a file system item and returns [code]True[/code] ifthe names match. 37 | 38 | So now, [code]ls[/code] is a list that contains the items thatprecede the item that we're searching for, [code]item[/code] isthat very item and [code]rs[/code] is the list of items that comeafter it in its folder. Now that we have this, we just present the item that wegot from [code]break[/code] as the focus and build a breadcrumbthat has all the data it needs. 39 | 40 | Note that if the name we're looking for isn't in the folder, the pattern [code]item:rs[/code] will try to match on an empty list and we'llget an error. Also, if our current focus isn't a folder at all but a file, weget an error as well and the program crashes. 41 | 42 | Now we can move up and down our file system. Let's start at the root and walk tothe file [code]"skull_man(scary).bmp"[/code]: 43 | 44 | [code]newFocus[/code] is now a zipper that's focused on the [code]"skull_man(scary).bmp"[/code] file. Let's get the firstcomponent of the zipper (the focus itself) and see if that's really true: 45 | 46 | Let's move up and then focus on its neighboring file[code]"watermelon_smash.gif"[/code]: 47 | 48 | - Manipulating our file system 49 | 50 | Now that we know how to navigate our file system, manipulating it is easy.Here's a function that renames the currently focused file or folder: 51 | 52 | Now we can rename our [code]"pics"[/code] folder to 53 | 54 | [code]"cspi"[/code]: 55 | 56 | We descended to the [code]"pics"[/code] folder, renamed it andthen moved back up. 57 | 58 | How about a function that makes a new item in the current folder? Behold: 59 | 60 | Easy as pie. Note that this would crash if we tried to add an item but weren'tfocusing on a folder, but were focusing on a file instead. 61 | 62 | Let's add a file to our [code]"pics"[/code] folder and then moveback up to the root: 63 | 64 | What's really cool about all this is that when we modify our file system, itdoesn't actually modify it in place but it returns a whole new file system. That way, wehave access to our old file system (in this case, [code]myDisk[/code]) as well as the new one (the first component of [code]newFocus[/code]). So by using zippers, we get versioning for free, meaning that we can alwaysrefer to older versions of data structures even after we've changed them, so tospeak. This isn't unique to zippers, but is a property of Haskell because itsdata structures are immutable. With zippers however, we get the ability toeasily and efficiently walk around our data structures, so the persistence ofHaskell's data structures really begins to shine. -------------------------------------------------------------------------------- /cap07-modulos/cap07-part04.md: -------------------------------------------------------------------------------- 1 | Data.Map 2 | ======== 3 | 4 | Listas de associação (também chamadas de dicionários) são listas usadas para armazenar pares de 5 | chave e valor, onde sua ordem não é considerada. Por exemplo, podemos usar uma lista de associação para 6 | guardar números de telefone (que seriam os valores) junto do nome dos donos (chaves). A ordem que 7 | esses dados estão armazenados não importa para nós, desde que consigamos o número certo ao pesquisar 8 | por uma pessoa. 9 | 10 | O modo mais óbvio de armazenar listas de associação em Haskell seria uma lista de pares. Onde o primeiro 11 | componente do par deve ser a chave, e o segundo componente o valor. Aqui vai um exemplo de uma lista de 12 | associação com números de telefone: 13 | 14 | 15 | Tirando a identação bizarra, isso é apenas uma lista de pares de strings. O objetivo mais comum ao lidar com 16 | listas de associação é a busca por chave. Vamos criar uma função que procura por uma dada chave. 17 | 18 | 19 | Extremamente simples. A função recebe uma chave e uma lista, filtra a lista para que permaneçam apenas 20 | os que têm chave correspondente e retorna o primeiro par chave-valor. Mas o que acontece ao procurarmos 21 | por uma chave inexistente? Bem, nós tentaríamos pegar o primeiro elemento de uma lista vazia, 22 | gerando um runtime error. Mas precisamos fazer os nossos programas para que sejam um pouco mais difíceis 23 | de quebrar, então usaremos o tipo [code]Maybe[/code]. Se não encontrar uma chave, retornará 24 | [code]Nothing[/code]. Se encontrar, retornará [code]Just alguma-coisa[/code], onde "alguma-coisa" é o 25 | valor correspondente à chave. 26 | 27 | 28 | Preste atenção à declaração de tipo. Ela recebe uma chave que pode ser igualada, uma lista de associação e 29 | talvez devolva um valor. Parece correto. 30 | 31 | Essa é uma função de agenda de contatos que trabalha com uma lista. Temos a condição limite, 32 | quebramos a lista em uma cabeça e uma cauda, temos chamadas recursivas, esta tudo ali. 33 | Esse é o caso clássico de uso do padrão de folding. Vamos tentar implementar então com folds. 34 | 35 | 36 | Dica: Geralmente é melhor usar folds para listas de padrões recursivos ao invés de escrever 37 | explicitamente a recursão porque é mais fácil de se ler. Todos sabem o que é um fold ao ver a chamada à 38 | função [code]foldr[/code], mas toma mais tempo para entender todo seu processo de recursão. 39 | 40 | 41 | Funciona que é uma beleza! Agora só falta você usar sua nova agenda para ver se não fica só na pesquisa. 42 | 43 | Nós acabamos de implementar a função [code]lookup[/code] do [code]Data.List[/code]. Se quisermos 44 | achar um valor correspondente a uma chave, precisamos percorrer todos os elementos até encontrá-lo. 45 | O módulo [code]Data.Map[/code] oferece listas de associação muito mais rápidas (porque elas são 46 | implementadas internamente com árvores) e ainda provê algumas funções úteis. De agora em diante 47 | usaremos mapas ao invés de listas de associação. 48 | 49 | Já que [code]Data.Map[/code] exporta funções que conflitam com outras de [code]Prelude[/code] e 50 | [code]Data.List[/code], faremos uma importação qualificada. 51 | 52 | 53 | Coloque esse comando para importação no script e carregue-o via GHCI. 54 | 55 | Chega de enrolar e vamos ver o que o [code]Data.Map[/code] nos guarda! Aí vai um resumo rápido de 56 | suas funções. 57 | 58 | A função [function]fromList[/function] recebe uma lista de associações (na forma de uma lista mesmo) 59 | e retorna um mapa com a mesma estrutura. 60 | 61 | 62 | Se existirem chaves duplicadas na lista de associação original, as duplicadas são descartadas. 63 | Essa é a assinatura de tipo de [code]fromList[/code]. 64 | 65 | 66 | Ela diz que fromList recebe uma lista de pares de tipos [code]k[/code] e [code]v[/code] e retorna um mapa com 67 | chaves de tipo [code]k[/code] e tipo [code]v[/code]. Perceba que quando você está criando listas 68 | de associação com listas, as chaves só precisam ser possíveis de serem igualadas (terem tipos 69 | pertencentes à typeclass [code]Eq[/code]) mas não necessariamente ordenáveis. Essa é a essência 70 | do módulo [code]Data.Map[/code]. Ele precisa que as chaves sejam ordenáveis para organizá-las na 71 | forma de uma árvore. 72 | 73 | É sempre aconselhável usar o [code]Data.Map[/code] para associações chave-valor, a menos que o tipo 74 | não esteja na typeclass [code]Ord[/code]. 75 | 76 | [function]empty[/function] representa um mapa vazio. Não necessita de nenhum argumento, só retorna 77 | um mapa vazio. 78 | 79 | 80 | [function]insert[/function] recebe uma chave, um valor e um mapa e retorna um novo mapa igual ao 81 | antigo, mas com chaves e valor adicionados. 82 | 83 | 84 | Nós podemos implementar nosso próprio [code]fromList[/code] usando um mapa vazio, 85 | [code]insert[/code] e um fold. Veja só: 86 | 87 | 88 | Um fold bastante simples. Começamos com um mapa vazio e dobramos à direita, inserindo os pares de 89 | chave-valor no acumulador em cada passo. 90 | 91 | [function]null[/function] testa se um mapa está vazio. 92 | 93 | 94 | [function]size[/function] reporta o tamanho de um mapa. 95 | 96 | 97 | [function]singleton[/function] recebe uma chave e um valor e cria um mapa com apenas esses dados. 98 | 99 | 100 | [function]lookup[/function] funciona como o [code]Data.List[/code] [code]lookup[/code], mas 101 | opera apenas em mapas. Retorna [code]Just something[/code] se encontrar algo pela chave procurada, 102 | [code]Nothing[/code] se encontrar nada. 103 | 104 | [function]member[/function] é um predicado que recebe uma chave e um mapa e diz se a chave existe 105 | ou não no mapa. 106 | 107 | 108 | [function]map[/function] e [function]filter[/function] funcionam semelhantemente aos homônimos 109 | das listas. 110 | 111 | 112 | 113 | [function]toList[/function] é o inverso da [code]fromList[/code]. 114 | 115 | 116 | [function]keys[/function] e [function]elems[/function] retornam listas de chaves e valores, 117 | respectivamente. [code]keys[/code] é o equivalente a [code]map fst . Map.toList[/code] e 118 | [code]elems[/code] é o de [code]map snd . Map.toList[/code]. 119 | 120 | [function]fromListWith[/function] é uma função muito legal. Age como [code]fromList[/code], mas 121 | ao invés de discartar as chaves duplicadas, recebe também uma função que decide o que fazer com elas. 122 | Imagine que uma garota pode ter vários números e temos uma lista de associação como essa: 123 | 124 | 125 | Agora se quisermos usar [code]fromList[/code] para criar um mapa, perderemos alguns números! Então é 126 | assim que vamos fazer: 127 | 128 | 129 | Se uma chave duplicada for encontrada, a função que passamos será usada para combinar os valores das 130 | chaves em outro valor. Poderíamos primeiro ter listas únicas para os valores e usarmos [code]++[/code] 131 | para fazer a combinação. 132 | 133 | 134 | Muito elegante! Outro caso de uso é quando criamos um mapa com base em uma lista de associação de números 135 | e quando encontramos uma chave duplicada, queremos o maior valor da chave para mante-lo. 136 | 137 | 138 | Ou podemos optar por adicionar valores junto a mesma chave. 139 | 140 | 141 | [function]insertWith[/function] é para [code]insert[/code] o que [code]fromListWith[/code] é para 142 | [code]fromList[/code]. Ele insere um par chave-valor em um mapa, porém se o mapa já tiver a chave, 143 | ele usa a função passada para determinar o que fazer. 144 | 145 | 146 | Estas são apenas algumas funções do [code]Data.Map[/code]. Você pode ainda ver uma lista completa de funções na 147 | documentação. 148 | -------------------------------------------------------------------------------- /cap08-criando-tipos-typeclasses/cap08-part01.md: -------------------------------------------------------------------------------- 1 | Criando seus próprios tipos e typeclasses 2 | ========================================= 3 | 4 | Nos capítulos anteriores, vimos alguns tipos e typeclasses de Haskell. Neste capítulo, aprenderemos como criar nossos próprios e colocá-los para funcionar! 5 | 6 | ** Introdução a tipos de dados algébricos ** 7 | 8 | Até agora, vimos vários tipos de dados. [code]Bool[/code], [code]Int[/code], [code]Char[/code], [code]Maybe[/code], etc. Mas como criar nosso próprio? Bom, um jeito é usar a palavra-chave data para definir um tipo. Vamos ver como o tipo [code]Bool[/code] é definido na biblioteca padrão. 9 | [code]data[/code] significa que estamos definindo um novo tipo de dados. As partes anterior a [code]=[/code] diz o tipo, que é [code]Bool[/code]. As de depois são value constructors (construtores de valores). Elas especificam os diferentes valores que o tipo pode assumir. O [code]|[/code] pode ser lido como ou (OR). Então você pode ler tudo como: o tipo [code]Bool[/code] pode ter valores [code]True[/code] ou [code]False[/code]. Ambos, nome do tipo e contrutores de valores devem começar com letras maiúsculas. 10 | 11 | Da mesma maneira, podemos imaginar o tipo [code]Int[/code] sendo definido como: 12 | 13 | 14 | Os primeiro e último construtores são os valores menores e maiores possíveis de [code]Int[/code]. Não é definido exatamente assim, as reticências estão aí apenas com propósitos ilustrativos já que omitimos uma grande quantidade de números. 15 | 16 | Agora, vamos pensar como podemos representar uma forma geométrica em Haskell. Um jeito é usar tuplas. Um círculo pode ser algo como [code](43.1, 55.0, 10.4)[/code] onde o primeiro e o segundo campos são as coordenadas do centro do círculo e o terceiro é o raio. Parece bom, mas também serve para representar qualquer vetor 3D ou algo do gênero. A melhor solução seria dizer qual é a forma, se um círculo ou retângulo. Aí: 17 | 18 | 19 | E agora? Acho que gostei disso. O value constructor [code]Circle[/code] tem três campos float. Então quando escrevemos um value constructor, nós podemos opcionalmente adicionar tipos que o nosso novo tipo conterá. Aqui, os dois primeiros campos são as coordenadas do centro, o terceiro é o raio. O value constructor [code]Rectangle[/code] tem quatro campos que aceitam floats. Os dois primeiros são as coordenadas do ponto superior esquerdo e os outros dois coordenadas do inferior direito. 20 | 21 | 22 | Mas quando falo campos, quero dizer parâmetros. Value constructors são basicamente funções que retornam um valor em um tipo de dados. Vejamos como estão declarados os tipos desses dois construtores. 23 | 24 | 25 | Legal, então value constructors são funções como todo o resto. Quem imaginaria? Vamos fazer uma função que recebe uma forma geométrica e que retorna a sua superfície. 26 | 27 | 28 | A primeira coisa digna de nota é a declaração de tipo. Ela nos diz que uma função recebe uma forma geométrica e retorna um float. Nós não poderíamos declarar o tipo como [code]Circle -> Float[/code] porque [code]Circle[/code] não é um tipo, [code]Shape[/code] é. Assim como não podemos escrever uma função declarando o seu tipo como [code]True -> Int[/code]. A próxima coisa que vemos aqui é que podemos usar pattern match em construtores. Nós já fizemos isso (várias vezes por sinal) ao testar patterns por [code][][/code] ou [code]False[/code] ou [code]5[/code], com a diferença que não haviam tantos campos. Nós apenas escrevemos o construtor e associamos seus campos a nomes. Como estamos interessados no raio, os dois primeiros campos não são importantes, que nos dizem onde o círculo está. 29 | 30 | Ei, funciona! Mas se tentarmos escrever [code]Circle 10 20 5[/code] no prompt, teremos um erro. Isso é devido ao Haskell não saber mostrar nosso tipo de dado como string (ainda). Lembre-se, quando tentamos mostrar um valor na tela, Haskell primeiro executa a função [code]show[/code] para conseguir a versão em string do nosso valor e mostrar no terminal. Para fazer o nosso tipo [code]Shape[/code] parte da typeclass [code]Show[/code] nós o modificamos desse modo: 31 | 32 | 33 | Não vamos ir muito a fundo com o uso do deriving por enquanto. Digamos que se adicionarmos [code]deriving (Show)[/code] no fim da declaração de data, Haskell automaticamente fará esse tipo parte da typeclass [code]Show[/code]. Logo, podemos fazer assim: 34 | 35 | Value constructors são funções, então podemos mapear e aplicá-los parcialmente. Se quisermos ter uma lista de círculos com diferentes raios, podemos fazer assim. 36 | 37 | Nosso tipo de dados está bom, mas poderia estar melhor. Vamos criar um tipo de dados intermediário que define um ponto em um espaço bidimensional e então deixar nossas formas geométricas mais fáceis de se entender. 38 | 39 | Perceba que quando definimos um ponto, usamos o mesmo nome para o tipo de dados e o value constructor. Isso não tem nenhum funcionamento especial, mas é um padrão informal para quando o value constructor só pode assumir um valor. Agora que o [code]Circle[/code] tem dois campos, um deles do tipo [code]Point[/code] e outro [code]Float[/code]. Isso torna as coisas mais claras. E o mesmo para o retângulo. Só temos que modificar a função [code]surface[/code] para passar a refletir as mudanças. 40 | 41 | A única coisa que tivemos que mudar foram os patterns. A parte que se refere ao círculo permanece igual. No pattern do retângulo, nós usamos pattern matching aninhadamente para conseguir as medidas das linhas da figura. Se quiséssemos os próprios pontos por alguma razão, poderíamos ter usado as-patterns. 42 | 43 | Que tal uma função que move uma figura? Recebe uma figura, o tanto a se mover no eixo x e no eixo y para retornar uma nova figura de mesmas dimensões, localizada em outra posição. 44 | 45 | 46 | Bastante simples. Nós somamos a quantidade de movimento à cada um dos pontos da figura. 47 | 48 | 49 | Se não quisermos mexer diretamente com pontos, podemos criar funções auxiliares que criam figuras de qualquer tamanho nas coordenadas zero e a partir daí movê-las. 50 | 51 | 52 | Você pode, é claro, exportar os tipos de dados em seus módulos. Para isso, apenas escreva o tipo junto das funções que está exportando e, entre parênteses, especifique os tipos dos construtores que devem ser exportados, separados por vírgulas. Se quiser exportar todos os construtores de valores de um tipo, apenas escreva [code]..[/code]. 53 | 54 | Se queremos exportar as funções e tipos que definimos no módulo, podemos usar algo como isso: 55 | 56 | Com o [code]Shape(..)[/code], exportamos todos os construtores de valores de [code]Shape[/code], o que significa que quem importar nosso módulo poderá criar formas usando os construtores de valores [code]Rectangle[/code] e [code]Circle[/code]. É o mesmo que escrever [code]Shape (Rectangle, Circle)[/code]. 57 | 58 | Poderíamos ainda optar por exportar nenhum dos construtores de valores de [code]Shape[/code], escrevendo [code]Shape[/code] no código de exportação. Desse modo, quem importasse o nosso módulo poderia criar formas usando as funções auxiliares [code]baseCircle[/code] e [code]baseRect[/code]. [code]Data.Map[/code] funciona assim. Você não pode criar um mapa usando [code]Map.Map [(1,2),(3,4)][/code] porque não exporta o construtor de valor. De qualquer maneira, pode usando funções auxiliares como [code]Map.fromList[/code]. Lembre-se, construtores de valores nada mais são do que funções que recebem campos como parâmetros e retornam valores do mesmo tipo (como [code]Shape[/code]) e seu retorno. Então quando decidimos não exportá-los, proibimos que alguém importando nosso módulo os use. Mas se outras funções não retornam um tipo, podemos usá-las para criar valores dos tipos de dados costumizados. 59 | 60 | Não exportar os construtores de valores de tipos de dados os fazem mais abstratos, de modo que escondemos a sua implementação. Vale ressaltar que quem importar nosso módulo não pode usar pattern match em construtores de valores. 61 | -------------------------------------------------------------------------------- /cap08-criando-tipos-typeclasses/cap08-part04.md: -------------------------------------------------------------------------------- 1 | Instâncias derivadas 2 | ==================== 3 | 4 | Na sessão Basicão de Typeclasses, nós mostramos as bases das typeclasses. Nós mostramos que a typeclass é uma espécie de interface que define algum comportamento. Um tipo pode criar uma instância de uma typeclass se ele suportar esse comportamento. Exemplo: o tipo [code]Int[/code] é uma instância da typeclass [code]Eq[/code] pois a typeclass [code]Eq[/code] define o comportamento de coisas que podem ser comparadas. E como inteiros podem ser comparados, [code]Int[/code] é parte da typeclass [code]Eq[/code]. A real utilidade vêm com funções que agem como uma interface para [code]Eq[/code], chamada [code]==[/code] e [code]/=[/code]. Se um tipo é parte da typeclass [code]Eq[/code], nós podemos usar a função [code]==[/code] com valores desse tipo. Esse é porque expressões como [code]4 == 4[/code] e [code]"foo" /= "bar"[/code] tem os tipos checados. 5 | 6 | Nós também mencionamos que eles são geralmente confundidos com classes em linguagens como Java, Python, C++ e similares, o que confunde muitas pessoas. Nessas linguagens, classes são como um modelo pelo qual nós criamos objetos que contém estado e pode realizar algumas ações. Typeclasses são mais como interfaces. Nós não construímos dados a partir de typeclasses. Em vez disso, primeiro construímos nosso tipo de dado e então nós pensamos como ele pode agir. Se ele pode agir como algo que pode ser comparado, nós fazemos dele uma instância da typeclass [code]Eq[/code. Se ele pode agir como algo que pode ser ordenado, nós fazemos dele uma instância da typeclass [code]Ord[/code]. 7 | 8 | Na próxima sessão, vamos dar uma olhada como podemos manualmente fazer nossas instâncias de tipos de uma typeclass implementando funções definidas pela typeclass. Mas por enquanto vamos ver como Haskell pode automaticamente fazer do nosso tipo uma instância de alguma das seguintes typeclasses: [code]Eq[/code], [code]Ord[/code], [code]Enum[/code], [code]Bounded[/code], [code]Show[/code], [code]Read[/code]. Haskell pode derivar o comportamento dos nossos tipos nesse contexto se nós usarmos a palavra reservada deriving quando construímos o nosso tipo. 9 | 10 | Considere este tipo de dados: 11 | 12 | 13 | 14 | Ele descreve uma pessoa. Vamos assumir que não existe duas pessoas com a mesma combinação de primeiro nome, sobrenome e idade. Agora, se nós temos registros de duas pessoas, faz algum sentido ver se eles representam a mesma pessoa? Sim, faz. Nós podemos tentar comparar ambas e ver que elas são ou não iguais. É por isso que não faz sentido para esse tipo fazer parte da [code]Eq[/code]. Nós vamos derivar a instância. 15 | 16 | 17 | Quando nós derivamos uma instância de [code]Eq[/code] para um tipo e tentamos comparar os dois valores desse tipos com [code]==[/code] ou [code]/=[/code], Haskell verá se o valor dos construtores são correspondentes (há apenas um único construtor cujo valor passa) e então ele irá checar se todos os dados internos correspondem testando cada par de campos com [code]==[/code]. Há apenas um problema, os tipos de todos os campos também tem de fazer parte da typeclass [code]Eq[/code]. Mas uma vez que ambos são [code]String[/code] e [code]Int[/code], então OK. Vamos testar nossa instância de [code]Eq[/code]. 18 | 19 | 20 | 21 | Claro, uma vez que [code]Person[/code] agora está em [code]Eq[/code], nós podemos usá-lo como [code]a[/code] para todas as funções que tem a restrição de class para [code]Eq a[/code] em sua assinatura, como [code]elem[/code]. 22 | 23 | 24 | 25 | As typeclasses [code]Show[/code] e [code]Read[/code] são para coisas que podem ser convertidas de e para strings, respectivamente. Assim como [code]Eq[/code], se o construtor de um tipo tiver campos, seu tipo precisa fazer parte de [code]Show[/code] ou [code]Read[/code] se nós quisermos fazer do nosso tipo uma instância deles. Vamos fazer nosso tipo de dados [code]Person[/code] também fazer parte de [code]Show[/code] e [code]Read[/code]. 26 | 27 | 28 | 29 | Agora nós podemos imprimir uma pessoa no terminal. 30 | 31 | 32 | 33 | Se nós tentássemos imprimir uma pessoa no terminal antes de tornar o tipo [code]Person[/code] parte de [code]Show[/code], Haskell teria reclamado para nós, afirmando que ele não sabia como representar uma pessoa como uma string. Mas agora que nós derivamos uma instância de [code]Show[/code] para ela, ele saberá como. 34 | 35 | [code]Read[/code] é praticamente a typeclass inversa à [code]Show[/code]. [code]Show[/code] é para conversão de valores de um tipo em uma string, [code]Read[/code] é para conversão de strings para valores de um tipo. Lembre-se, quando nós usamos a função [code]read[/code], nós temos que usar uma notação de tipo explicita para dizer ao Haskell qual o tipo que nós queremos pegar como resultado. Se nós não explicitamos o tipo que queremos como resultado, Haskell não vai saber que tipo nós queremos. 36 | 37 | 38 | Se usarmos depois o resultado do [code]read[/code] de um jeito que Haskell possa inferir que isto deve ser uma pessoa, nós não temos que usar nenhuma notação de tipo. 39 | 40 | 41 | 42 | Também podemos ler tipos parametrizados, mas nós temos que preencher os tipos dos parâmetros. Então não podemos fazer [code]read "Just 't'" :: Maybe a[/code], porém podemos fazer [code]read "Just 't'" :: Maybe Char[/code]. 43 | 44 | Instâncias derivadas de [code]Ord[/code] agem como o esperado. Primeiro os construtores são comparados lexicograficamente e se o valor dos dois construtores forem os mesmos, seus campos são comparados, admitindo que os tipos dos seus campos também são instâncias de [code]Ord[/code]. O tipo [code]Bool[/code] pode ter o valor tanto de [code]False[/code] ou [code]True[/code]. Com a finalidade de ver como ele se comporta quando comparado, nós podemos pensar que ele é implementado da seguinte forma: 45 | 46 | 47 | Por causa do construtor de valor [code]False[/code] ser especificado primeiro e o construtor de valor [code]True[/code] ser especificado logo em seguida, nós podemos considerar [code]True[/code] como maior que [code]False[/code]. 48 | 49 | 50 | 51 | No tipo [code]Maybe a[/code], o construtor de valor [code]Nothing[/code] é especificado antes do construtor de valor [code]Just[/code], portanto o valor de [code]Nothing[/code] é sempre menor que o valor de [code]Just algumacoisa[/code], mesmo que algumacoisa seja menos um bilhão de trilhões. Mas se nós compararmos dois valores [code]Just[/code], então será comparado com o que há dentro deles. 52 | 53 | 54 | 55 | Mas nós não podemos fazer algo como [code]Just (*3) > Just (*2)[/code], pois [code](*3)[/code] e [code](*2)[/code] são funções que não são instâncias de [code]Ord[/code]. 56 | 57 | Nós podemos facilmente usar tipos algébricos para fazer enumerações e as typeclasses [code]Enum[/code] e [code]Bounded[/code] nos ajudam com isso. Considere o seguinte tipo: 58 | 59 | 60 | Por causa de todos os valores de construtores serem nulos (não pegam parâmetros, ou seja, campos), nós podemos fazer com que sejam parte da typeclass [code]Enum[/code]. A typeclass [code]Enum[/code] é pra coisas que tem predecessor e sucessor. Nós podemos faze-los também parte da typeclass [code]Bounded[/code], que é para coisas que tem um menor e um maior valor possível. E enquanto estamos no assunto, vamos torná-lo uma instância de todos as outras typeclasses derivadas e ver o que podemos fazer com ele. 61 | 62 | 63 | Por ele ser parte das typeclasses [code]Show[/code] e [code]Read[/code], nós podemos converter os valores desse tipo, de e para strings. 64 | 65 | 66 | Por ele ser parte das typeclasses [code]Eq[/code] e [code]Ord[/code], nós podemos comparar ou equiparar dias. 67 | 68 | 69 | Ele também é parte de [code]Bounded[/code], portanto nós podemos pegar o menor e o maior dia. 70 | 71 | 72 | 73 | Ele também é uma instância de [code]Enum[/code]. Nós podemos pegar os predecessores e sucessores dos dias e fazer listas de intervalos com eles. 74 | 75 | 76 | 77 | Isso é bem impressionante. -------------------------------------------------------------------------------- /cap12-punhado-de-monads/cap12-part07.md: -------------------------------------------------------------------------------- 1 | Monad laws 2 | ========== 3 | 4 | Just like applicative functors, and functors before them, monads come with a few laws that all monad instances must abide by. Just because something is made an instance of the [code]Monad[/code] type class doesn't mean that it's a monad, it just means that it was made an instance of a type class. For a type to truly be a monad, the monad laws must hold for that type. These laws allow us to make reasonable assumptions about the type and its behavior. 5 | 6 | Haskell allows any type to be an instance of any type class as long as the types check out. It can't check if the monad laws hold for a type though, so if we're making a new instance of the [code]Monad[/code] type class, we have to be reasonably sure that all is well with the monad laws for that type. We can rely on the types that come with the standard library to satisfy the laws, but later when we go about making our own monads, we're going to have to manually check the if the laws hold. But don't worry, they're not complicated. 7 | 8 | 9 | Left identity 10 | 11 | The first monad law states that if we take a value, put it in a default context with [code]return[/code] and then feed it to a function by using [code]>>=[/code], it's the same as just taking the value and applying the function to it. To put it formally: 12 | 13 | - [law]return x >>= f[/code] is the same damn thing as [law]f x[/code] 14 | 15 | If you look at monadic values as values with a context and [code]return[/code] as taking a value and putting it in a default minimal context that still presents that value as its result, it makes sense, because if that context is really minimal, feeding this monadic value to a function shouldn't be much different than just applying the function to the normal value, and indeed it isn't different at all. 16 | 17 | For the [code]Maybe[/code] monad [code]return[/code] is defined as [code]Just[/code]. The [code]Maybe[/code] 18 | monad is all about possible failure, and if we have a value and want to put it in such a context, it makes sense that we treat it as a successful computation because, well, we know what the value is. Here's some [code]return[/code] usage with [code]Maybe[/code]: 19 | 20 | For the list monad [code]return[/code] puts something in a singleton list. The [code]>>=[/code] implementation for lists goes over all the values in the list and applies the function to them, but 21 | since there's only one value in a singleton list, it's the same as applying the function to that value: 22 | 23 | We said that for [code]IO[/code], using [code]return[/code] makes an I/O action that has no side-effects but just presents a value as its result. So it makes sense that this law holds for [code]IO[/code] as well. 24 | 25 | 26 | Right identity 27 | 28 | The second law states that if we have a monadic value and we use [code]>>=[/code] to feed it to [code]return[/code], the result is our original monadic value. Formally: 29 | 30 | - [law]m >>= return[/code] is no different than 31 | just [law]m[/code] 32 | 33 | This one might be a bit less obvious than the first one, but let's take a look at why it should hold. When we feed monadic values to functions by using [code]>>=[/code], those functions take normal values and return monadic ones. [code]return[/code] is also one such function, if you consider its type. Like we said, [code]return[/code] puts a value in a minimal context that still presents that value as its result. This means that, for instance, for [code]Maybe[/code], it doesn't introduce any failure and for lists, it doesn't introduce any extra non-determinism. Here's a test run for a few monads: 34 | 35 | If we take a closer look at the list example, the implementation for [code]>>=[/code] is: 36 | 37 | So when we feed [code][1,2,3,4][/code] to [code]return[/code], first [code]return[/code] gets mapped over [code][1,2,3,4][/code], resulting in [code][[1],[2],[3],[4]][/code] and then this gets concatenated and we have our original list. 38 | 39 | Left identity and right identity are basically laws that describe how [code]return[/code] should behave. It's an important function for making normal values into monadic ones and it wouldn't be good if the monadic value that it produced did a lot of other stuff. 40 | 41 | 42 | Associativity 43 | 44 | The final monad law says that when we have a chain of monadic function applications with [code]>>=[/code], it shouldn't matter how they're nested. Formally written: 45 | 46 | - Doing [law](m >>= f) >>= g[/code] is just like doing [law]m >>= (\x -> f x >>= g)[/code] 47 | 48 | Hmmm, now what's going on here? We have one monadic value, [code]m[/code] and two monadic functions [code]f[/code] and [code]g[/code]. When we're doing [code](m >>= f) >>= g[/code], we're feeding [code]m[/code] to [code]f[/code], which results in a monadic value. Then, we feed that monadic value to [code]g[/code]. In the expression [code]m >>= (\x -> f x >>= g)[/code], we take a monadic value and we feed it to a function that feeds the result of [code]f x[/code] to [code]g[/code]. It's not easy to see how those two are equal, so let's take a look at an example that makes this equality a bit clearer. 49 | 50 | Remember when we had our tightrope walker Pierre walk a rope while birds landed on his balancing pole? To simulate birds landing on his balancing pole, we made a chain of several functions that might produce failure: 51 | 52 | We started with [code]Just (0,0)[/code] and then bound that value to the next monadic function, [code]landRight 2[/code]. The result of that was another monadic value which got bound into the next monadic function, and so on. If we were to explicitly parenthesize this, we'd write: 53 | 54 | But we can also write the routine like this: 55 | 56 | [code]return (0,0)[/code] is the same as [code]Just (0,0)[/code] and when we feed it to the lambda, the [code]x[/code] becomes [code](0,0)[/code]. 57 | 58 | [code]landRight[/code] takes a number of birds and a pole (a tuple of numbers) and that's what it gets passed. This results in a [code]Just (0,2)[/code] and when we feed this to the next lambda, [code]y[/code] is [code](0,2)[/code]. This goes on until the final bird landing produces a [code]Just (2,4)[/code], which is indeed the result of the whole expression. 59 | 60 | So it doesn't matter how you nest feeding values to monadic functions, what matters is their meaning. Here's another way to look at this law: consider composing two functions, [code]f[/code] and [code]g[/code]. Composing two functions is implemented like so: 61 | 62 | If the type of [code]g[/code] is [code]a -> b[/code] and the type of [code]f[/code] is [code]b -> c[/code], we arrange them into a new function which has a type of [code]a -> c[/code], so that its parameter is passed between those functions. Now what if those two functions were monadic, that is, what if the values they returned were monadic values? If we had a function of type [code]a -> m b[/code], we couldn't just pass its result to a function of type [code]b -> m c[/code], because that function accepts a normal [code]b[/code], not a monadic one. We could however, use [code]>>=[/code] to make that happen. So by using [code]>>=[/code], we can compose two monadic functions: 63 | 64 | So now we can compose two monadic functions: 65 | 66 | Cool. So what does that have to do with the associativity law? Well, when we look at the law as a law of compositions, it states that [law]f <=< (g <=< h)[/code] should be the same as [law](f <=< g) <=< h[/code]. This is just another way of saying that for monads, the nesting of operations shouldn't matter. 67 | 68 | If we translate the first two laws to use [code]<=<[/code], then the left identity law states that for every monadic function [code]f[/code], [law]f <=< return[/code] is the same as writing just [law]f[/code] and the right identity law says that [law]return <=< f[/code] is also no different from [law]f[/code]. 69 | 70 | This is very similar to how if [code]f[/code] is a normal function, [code](f . g) . h[/code] is the same as [code]f . (g . h)[/code], [code]f . id[/code] is always the same as [code]f[/code] and [code]id . f[/code] is also just [code]f[/code]. 71 | 72 | In this chapter, we took a look at the basics of monads and learned how the [code]Maybe[/code] monad and the list monad work. In the next chapter, we'll take a look at a whole bunch of other cool monads and we'll also learn how to make our own. -------------------------------------------------------------------------------- /cap03-tipos_typeclasses/cap03-part03.md: -------------------------------------------------------------------------------- 1 | Basicão de Typeclasses 2 | ====================== 3 | 4 | Uma Typeclass (classe de tipos) é como uma interface que define um comportamento. Se um tipo é parte de 5 | uma typeclass, quer dizer que ela suporta e implementa o comportamento especificado pela classe de tipo. 6 | Muita gente vinda da orientação a objetos se confunde e acha estar diante de uma classe de OO. 7 | Bom... não. Você pode pensar que são como as interfaces de Java, mas na verdade são muito melhor. 8 | 9 | Qual deve ser o tipo da função [code]==[/code]? 10 | 11 | Nota: o operador de igualdade ([code]==[/code]) é uma função. Assim como [code]+[/code], 12 | [code]*[/code], [code]-[/code], [code]/[/code] e quase todos os outros operadores. Se uma função é 13 | composta apenas de caracteres especiais, ela é por padrão uma função infixa. Se quisermos verificar o 14 | seu tipo, passe-a para outra função ou chame-a como função prefixa, colocando-a entre parênteses. 15 | 16 | 17 | Interessante. Temos algo novo aqui, o símbolo [code]=>[/code]. Tudo antes do símbolo 18 | [code]=>[/code] é denominado class constraint (restrição de classe). Podemos ler a declaração 19 | de tipo anterior assim: a função de igualdade recebe dois argumentos de mesmo tipo e retorna um 20 | [code]Bool[/code]. Esse tipo deve ser membro da classe [code]Eq[/code] (que é a class constraint). 21 | 22 | A typeclass [code]Eq[/code] provê uma interface para o teste de igualdade. Qualquer tipo que faça 23 | sentido ser verificado por igualdade com outro tipo deve estar na typeclass [code]Eq[/code]. Todos os 24 | tipos Haskell - exceto os de IO (tipo para lidar com entrada e saída) e funções - fazem parte da 25 | typeclass [code]Eq[/code]. 26 | 27 | A função [code]elem[/code] tem o tipo [code](Eq a) => a -> [a] -> Bool[/code] porque usa o 28 | operador [code]==[/code] para procurar um determinado elemento em uma dada lista. 29 | 30 | Algumas Typeclasses básicas: 31 | 32 | [class]Eq[/code] é usado por tipos que suportam teste por igualdade. As funções que fazem parte dela 33 | implementam [code]==[/code] e [code]/=[/code]. Se existe alguma class constraint de [code]Eq[/code] 34 | para um tipo variável em uma função, usa o operador [code]==[/code] ou [code]/=[/code] em algum lugar 35 | de sua definição. Todos os tipos já mencionados (com excessão de funções), são parte de [code]Eq[/code], 36 | então podem ser testados por igualdade. 37 | 38 | [class]Ord[/code] é para tipos que têm ordem. 39 | 40 | Todos os tipos já vistos (exceto funções) são parte de [code]Ord[/code]. [code]Ord[/code] engloba 41 | todas as funções de comparação comuns como [code]>[/code], [code]<[/code], [code]>=[/code] e 42 | [code]<=[/code]. A função [code]compare[/code] requer dois membros de [code]Ord[/code] de mesmo tipo 43 | e retorna sua ordenação. [type]Ordering[/code] é uma typeclass que pode ser [code]GT[/code], 44 | [code]LT[/code] ou [code]EQ[/code], significando maior que, menor que e igual a, 45 | respectivamente. 46 | 47 | Para ser membro de [code]Ord[/code], um tipo deve ser membro do prestigioso e restrito clube do 48 | [code]Eq[/code]. 49 | 50 | Membros do [class]Show[/code] podem ser representados como strings. Todos os tipos cobertos até agora 51 | (com exceção das funções) são suportados por [code]Show[/code]. A função que lida com a typeclass 52 | [code]Show[/code] mais usada é a [code]show[/code]. Ela recebe um valor de um que tipo presente em 53 | [code]Show[/code] e nos mostra esse valor como uma string. 54 | 55 | [class]Read[/code] é tipo uma oposição da typeclass [code]Show[/code]. A função [code]read[/code] 56 | recebe uma string e retorna um tipo membro de [code]Read[/code]. 57 | 58 | Até agora tudo simples. Todos os tipos já vistos estão nessas classes de tipo. Mas o que acontece ao 59 | tentarmos [code]read "4"[/code]? 60 | 61 | 62 | O que o GHCI está tentando nos dizer é que não sabe o que se esperar como retorno. Perceba que nos 63 | usos anteriores de [code]read[/code] nós sempre fazíamos algo com o resultado. Assim, o GHCI podia 64 | inferir o tipo esperado de [code]read[/code]. Se usassemos ele como um booleano, ele saberia que deveria 65 | retornar um [code]Bool[/code]. Mas agora ele só sabe que deve ser algum tipo da classe [code]Read[/code]. 66 | Vamos dar uma olhada na declaração de tipo de [code]read[/code]. 67 | 68 | 69 | Viu? Ele retorna um tipo parte de [code]Read[/code] mas como não usamos o resultado depois, ele não 70 | saberá qual tipo será. É por isso que podemos especificar explicitamente type annotations 71 | (anotações de tipos). Anotações de tipos servem para dizer qual tipo que você quer que uma 72 | expressão assuma. Fazemos isso adicionando [code]::[/code] no fim da expressão com o tipo desejado. 73 | Observe: 74 | 75 | Na maioria das expressões, o compilador já pode assumir qual deve ser o tipo das expressões. Mas 76 | acontece dele não saber se deve ser [code]Int[/code] ou [code]Float[/code] para uma expressão como 77 | [code]read "5"[/code]. Para ter certeza, Haskell deveria primeiro avaliar [code]read "5"[/code]. 78 | Mas como Haskell é uma linguagem estaticamente tipada, precisa saber o tipo de todas as expressões na 79 | hora da compilação (ou no caso do GHCI, interpretação). Então dizemos ao Haskell: "Ei, essa expressão 80 | é desse tipo, caso não saiba!". 81 | 82 | Os membros de [code]Enum[/code] são tipos que possuem uma seqüência. A maior vantagem da typeclass 83 | [code]Enum[/code] é poder ser usada em ranges de listas. Seus tipos têm sucessores e predecessores 84 | definidos, que podem ser conseguidos pelas funções [code]succ[/code] e [code]pred[/code]. Fazem parte 85 | dessa classe os tipos: [code]()[/code], [code]Bool[/code], [code]Char[/code], [code]Ordering[/code], 86 | [code]Int[/code], [code]Integer[/code], [code]Float[/code] e [code]Double[/code]. 87 | 88 | [class]Bounded[/code] são os tipos que possuem limites - máximo e mínimo. 89 | 90 | [code]minBound[/code] e [code]maxBound[/code] são diferenciados por ter tipo 91 | [code](Bounded a) => a[/code]. São constantes polimórficas. 92 | 93 | Todas tuplas não-vazias também estão em [code]Bounded[/code]. 94 | 95 | [class]Num[/code] é uma typeclass numérica. Seus membros têm a função de agir como números. Vamos ver 96 | o tipo de um número. 97 | 98 | Parece que todos os números são constantes polimórficas. Elas podem tomas a forma de qualquer tipo 99 | da typeclass [code]Num[/code]. 100 | 101 | Esses são os tipos da typeclass [code]Num[/code]. Se verificar o tipo de [code]*[/code], descobrirá 102 | que ela aceita qualquer número. 103 | 104 | Recebe três números do mesmo tipo. É por isso que [code](5 :: Int) * (6 :: Integer)[/code] resultará 105 | em erro e [code]5 * (6 :: Integer)[/code] funcionará e retornará um [code]Integer[/code], já que 5 106 | pode tomar a forma de um [code]Int[/code] ou de um [code]Integer[/code]. 107 | 108 | Para estar em [code]Num[/code], o tipo já deve estar em [code]Show[/code] e [code]Eq[/code]. 109 | 110 | [class]Integral[/code] também é uma typeclass numérica. Enquanto [code]Num[/code] inclui todos os 111 | números (reais e inteiros), [code]Integral[/code] apenas inteiros. Essa typeclass é composta por 112 | [code]Int[/code] e [code]Integer[/code]. 113 | 114 | [class]Floating[/code] inclui apenas números de ponto flutuante, então são [code]Float[/code] e 115 | [code]Double[/code]. 116 | 117 | 118 | Uma função muito útil para lidar com números é [function]fromIntegral[/code]. A declaração do seu 119 | tipo é [code]fromIntegral :: (Num b, Integral a) => a -> b[/code]. Assim, vemos que ela recebe 120 | um número inteiro e transforma-o em algo mais genérico. Isso é útil quando você precisa que tipos 121 | inteiros e ponto flutuante trabalhem juntos. Por exemplo, a função [code]length[/code] tem uma 122 | declaração de [code]length :: [a] -> Int[/code] ao invés de ter algo mais geral como 123 | [code](Num b) => length :: [a] -> b[/code]. Acho que está assim por razões históricas, o que, 124 | na minha opinião, é besteira. Ainda assim, se tentarmos somar o tamanho de uma lista 125 | ([code]length[/code]) com [code]3.2[/code] teremos um erro, pois não é possível somar um 126 | [code]Int[/code] com um número de ponto flutuante. Então para contornar, [code]fromIntegral 127 | (length [1,2,3,4]) + 3.2[/code] funciona perfeitamente. 128 | 129 | Note que [code]fromIntegral[/code] tem mais de um class constraint em sua declaração de tipo. 130 | Como pode ver, isso é válido, desde que estejam separados por vírgulas dentro de parênteses. 131 | -------------------------------------------------------------------------------- /cap08-criando-tipos-typeclasses/cap08-part03.md: -------------------------------------------------------------------------------- 1 | Tipos paramétricos 2 | ================== 3 | 4 | Um construtor de valor pode pegar alguns parâmetros de valores e então produzir um novo valor. Por exemplo, o construtor [code]Car[/code] pega três valores e produz um valor de carro. De maneira similar, construtores de tipo podem pegar tipos como parâmetros e produzir novos tipos. Isso pode parecer um pouco meta à primeira vista, mas não é muito complicado. Se você tem familiaridade com os templates de C++, você verá alguns pontos paralelos. Para se ter uma imagem clara de como os parâmetros de tipos funcionam na prática, vamos dar uma olhada em como um tipo que você já encontrou é implementado. 5 | 6 | 7 | O [code]a[/code] aqui é um parâmetro de tipo. E por haver um parâmetro de tipo envolvido, chamamos [code]Maybe[/code] um construtor de tipo. Dependendo do que quisermos que esse tipo de dado guarde quando não for [code]Nothing[/code], esse construtor de tipo pode acabar produzindo um tipo [code]Maybe Int[/code], [code]Maybe Car[/code], [code]Maybe String[/code], etc. Nenhum valor pode ter um tipo de apenas [code]Maybe[/code], isso porque ele não é um tipo por si só, é um construtor de tipo. Para que isso se torne um tipo real do qual um valor pode ser parte, ele tem que ter todos os seus parâmetros preenchidos. 8 | 9 | 10 | Então se passarmos [code]Char[/code] como o parâmetro de tipo para [code]Maybe[/code], obtemos o tipo de [code]Maybe Char[/code]. O valor [code]Just 'a'[/code] tem um tipo de [code]Maybe Char[/code], por exemplo. 11 | 12 | 13 | Você pode não saber disso, mas nós usamos um tipo que tem um parâmetro de tipo antes de usar [code]Maybe[/code]. Estamos falando do tipo lista. Apesar de haver algum açúcar sintático na jogada, o tipo lista pega um parâmetro para produzir um tipo concreto. Valores podem ter um tipo [code][Int][/code], um tipo [code][Char][/code], um tipo [code]String[/code], mas você não pode ter um valor que tenha um tipo de apenas [code][][/code]. 14 | 15 | 16 | Vamos brincar um pouco com o tipo [code]Maybe[/code] 17 | 18 | 19 | Parâmetros de tipo são úteis porque podemos criar diferentes tipos com eles dependendo de que espécie de tipos desejamos ter guardados no nosso tipo de dado. Quando fazemos [code]:t Just "haha"[/code], o mecanismo de inferência de tipo descobre que isso é do tipo [code]Maybe [Char][/code], porque se o [code]a[/code] no código [code]Just a[/code] for uma String, entãp o [code]a[/code] em [code]Maybe a[/code] também deve ser uma String. 20 | 21 | 22 | Perceba que o tipo de [code]Nothing[/code] é [code]Maybe a[/code]. Seu tipo é polimórfico. Se alguma função precisar de um [code]Maybe Int[/code] como parâmetro, podemos passar um Nothing, porque [code]Nothing[/code] não contém um valor, logo não importa. O tipo [code]Maybe a[/code] pode agir como um [code]Maybe Int[/code] se ele tiver de fazê-lo, assim como [code]5[/code] pode agir como um [code]Int[/code] ou um [code]Double[/code]. Similarmente, o tipo da lista vazia é [code][a][/code]. Uma lista vazia pode agir como um lista de qualquer coisa. Por isso que podemos fazer [code][1, 2, 3] ++ [][/code] e [code][!ha", "ha", "ha"] ++ [][/code]. 23 | 24 | 25 | Usar parâmetros de tipos é muito benéfico, mas apenas quando usá-los faz sentido. Geralmente nós usamos quando nossos tipos de dados pudessem funcionar, independentemente do tipo do valor que está guardado dentro de si, como no nosso tipo [code]Maybe a[/code]. Se nosso tipo se comportar como uma espécie de caixa, isso será bom para o utilizarmos. Poderíamos mudar nosso tipo de dado [code]Car[/code] assim: 26 | 27 | 28 | Para isso: 29 | 30 | 31 | Mas haveria algum benefício? A resposta é: provavelmente não, uma vez que acabaríamos apenas definindo funções que apenas funcionariam com o tipo [code]Car String String Int[/code]. Por exemplo, dada nossa primeira definição de [code]Car[/code], poderíamos criar uma função que exibe as propriedades do carro em um texto pequeno e legal. 32 | 33 | 34 | 35 | Uma função pequenina e fofinha! A declaração de tipo é fofa e funciona bem. E se [code]Car[/code] fosse [code]Car a b c[/code]? 36 | 37 | 38 | 39 | Teríamos de forçar essa função a usar o tipo de [code]Car[/code] de [code](Show a) => Car String String a[/code]. Você pode ver que a assinatura de tipo é mais complicada e o único benefício que nós obteríamos seria que agora podemos usar qualquer tipo que seja uma instância da typeclass [code]Show[/code] como o tipo para [code]c[/code]. 40 | 41 | 42 | 43 | Entretanto na vida real, nós acabaríamos usando [code]Car String String Int[/code] na maioria das vezes e então pareceria que parametrizar o tipo [code]Car[/code] não vale à pena. Nós geralmente utilizamos parâmetros de tipos quando o tipo que está contido dentro dos vários construtores de valor de tipos de dados não é realmente importante para o funcionamento do tipo. Uma lista de coisas é uma lista de coisas e não importa o tipo dessas coisas, a lista ainda funciona. Se quisermos somar uma lista de números, podemos especificar depois como a função de soma funciona especificamente para lista de números. O mesmo serve para [code]Maybe[/code]. [code]Maybe[/code] representa uma opção de tanto não se ter nada como se ter alguma coisa. Não importa o tipo dessa coisa. 44 | 45 | 46 | Outro exemplo de um tipo parametrizado que já encontramos por aí é [code]Map k v[/code] de [code]Data.Map[/code]. O [code]k[/code] é o tipo das chaves em um mapa e [code]v[/code] é o tipo dos valores. Esse é um bom exemplo de onde os parâmetros são muito úteis. Ter mapas parametrizados nos permite ter mapeamentos de qualquer tipo para qualquer outro tipo, contanto que o tipo da chave seja parte da typeclass [code]Ord[/code]. Se estivéssemos definindo um tipo mapeador, poderíamos adicionar uma restrição de typeclass na declaração data: 47 | 48 | 49 | 50 | Entretanto, existe uma convenção muito forte em Haskell para nunca adicionar restrições de typeclass na declaração de dados. . Por quê? Bem, porque não há muitos benefícios, mas acabamos escrevendo mais restrições de classe, até quando não precisamos delas. Se colocarmos ou não a restrição[code]Ord k[/code] na declaração data para [code]Map k v[/code], teríamos de colocar uma restrição nas funções que assumem que as chaves de um mapa podem ser ordenadas. Mas se não colocarmos a restrição na declaração do dado, não teremos de colocar [code]Ord k[/code] na declaração de tipos de função que não se importam se k pode ser ordenado ou não. Um exemplo de tal função é [code]toList[/code], que apenas pega um mapeamento e converte-o em uma lista associativa. Sua assinatura de tipo é [code]toList :: Map k a -> [(k, a)][/code]. Se [code]Map k v[/code] tivesse uma restrição de tipo na sua declaração data, o tpo para [code]toList[/code] teria de ser [code]toList :: (Ord k) => Map k a -> [(k, a)][/code], mesmo que a função não fizesse nenhuma comparação de ordem entre as chaves. 51 | 52 | Então não coloque restrições de tipo em declarações data mesmo se parecer fazer sentido, porque você terá de colocá-las nas declarações de função de qualquer forma. 53 | 54 | Vamos implementar um tipo de vetor 3D e adicionar algumas operações para ele. Usaremos um tipo parametrizado porque mesmo sabendo que, em geral, o vetor conterá números, ainda vale a pena dar suporte a vários deles. 55 | 56 | 57 | 58 | [code]vplus[/code] é para adicionar dois vetores. Dois vetores são adicionados apenas somando seus respectivos componentes.[code]scalarMult[/code] é para o produto escalar entre dois vetores e [code]vectMult[/code] é para multiplicar um vetor por um escalar. Essas funções podem operar nos tipos de [code]Vector Int[/code], [code]Vector Integer[/code], [code]Vector Float[/code], qualquer coisa, contanto que [code]a[/code] de [code]Vector a[/code] seja da typeclas [code]Num[/code]. Além disso, se você examinar a declaração de tipo dessas funções, você verá que elas apenas podem operar em vetores do mesmo tipo e os números envolvidos devem ser do mesmo tipo que está contido dentro dos vetores. Perceba que não colocamos uma restrição de classe [code]Num[/code] na declaração data, uma vez que teríamos que repeti-la nas funções de qualquer forma. 59 | 60 | Mais uma vez, é importante distinguir entre o construtor de tipo e o construtor de valor. Quando estamos declarando um tipo de dado, a parte antes do [code]=[/code] é o construtor de tipo e os construtores depois dele (possivelmente separados por [code]|[/code]'s) são construtores de valor. Dar um tipo [code]Vector t t t -> Vector t t t -> t[/code] à uma função seria errado porque temos que colocar os tipos na declaração de tipo e o construtor de tipo vetor recebe apenas um parâmetro, enquanto que o construtor de valor recebe três. Vamos brincar com nossos vetores. 61 | --------------------------------------------------------------------------------