# Haskell/Tipos básicos

* Tipos básicos
* Introdução
* Primeiro passo
* Variáveis e funções
* Verdadeiro ou falso
* Tipos básicos
* Listas e n-uplas
* Tipos básicos II
* Casamento de padrões, if e let
* Composição de funções
* Entradas e saídas simples
Em programação, tipo são usados para agrupar valores similares, com características similares, dentre de uma certa categoria. Em Haskell, seu sistema de tipos é uma ferramenta poderosa usada para evitar muitos erros triviais dentro de um programa.

## Introdução

Quando se cria um programa, geralmente temos que lidar com várias entidades diferentes. Por exemplo, a operação $2+3$. O que são 2 e 3? Números. E o sinal de mais no meio deles? Não é um número, e trata-se de um símbolo que representa a operação de adição que queremos fazer entre 2 e 3.
Agora, imagine um programa que lhe peça seu nome, e o cumprimente com uma mensagem de "Olá". Nem seu nome, nem a mensagem são números, mas trata-se de palavras. São, portanto, texto. Em programação, quando nos referimos a texto, geralmente dizemos String, que significa "fio" ou "cadeia", em inglês. É uma forma curta de dizer "uma cadeia de caracteres".
Um banco de dados é um bom exemplo para ilustrar os diferentes tipos de dados possíveis. Imagine que temos uma tabela de contatos, como uma agenda. O conteúdo poderia ser o seguinte:
| Primeiro nome | Sobrenome | Endereço de correspondência | Número do telefone |
| João | Romão | Ladeira do Livramento, 77, Cortiço, Rio de Janeiro | 218444 |
| Severino | Retirante | Av. Capibaribe-Rio, 1025, Recife | 813586 |
Os campos de cada entrada possuem um valor diferente. João é um valor. Ladeira do Livramento, 77, Cortiço, Rio de Janeiro e 218444 são outros dois. Em termos de seus tipos, "Primeiro nome" e "Sobrenome" são textos, então são do tipo String.
À primeira vista, poderíamos classificar "Endereço de correspondência" também como texto. Entretanto, há diferentes formas de se interpretar um endereço como este. O primeiro nome pode ser o nome do prédio, ou o nome da rua, enquanto números subsequentes podem ser o número do imóvel ou de seu lote escriturado. E as palavras seguintes podem ser o nome do bairro ou da cidade. É possível também que o endereço comece por "Caixa Postal", e neste caso, ele sequer indica um endereço final, mas apenas uma caixa de correspondência em alguma agência postal. Portanto, cada parte do endereço tem um significado próprio.
A princípio poderíamos dizer que o endereço trata-se de um String. De fato, ele o é, mas aí perderíamos muitas informações importantes. Quando algo é definido com String, dizemos que se trata de uma simples sequencia de caracteres, enquanto que, se pudermos encaixá-lo numa categoria mais específica, teremos bem mais informações a cerca de seu conteúdo. Por exemplo, se soubermos que um String trata-se de um Endereço, já temos muito mais informações a seu respeito, e podemos agora interpretá-lo e manipulá-lo de maneira mais apropriada.
Essa mesma lógica também pode ser aplicada aos números de telefone. Podemos especificar um tipo chamado NúmeroTelefone e assim, quando houver algum número desse tipo, poderíamos buscar padrões como códigos de área ou de país. Um outro motivo para não tratá-los como números comuns, é que não convém fazermos operações artiméticas com eles: o que você espera conseguir ao multiplicar um NúmeroTelefone por 100, ou dividí-lo pela metade? É possível que o resultado nem seja um número de telefone válido. Além disso, não é aceitável perder, omitir ou arredondá-los pelo mesmo motivo.

### A utilidade dos tipos

Mas por que é que separar e categorizar coisas nos ajuda a programar? Uma vez que temos os tipos definidos, podemos especificar o que se pode ou não fazer com os valores. Isso ajuda bastante a evitar erros quando tempos programas muito extensos.

## Usando o comando interativo :type

Vamos explorar o funcionamento dos tipos usando uma sessão do GHCi. O tipo de uma expressão pode ser verificado usando-se o comando :type, ou :t. Tente com booleanos e expressões booleanas:
O símbolo :: aparece em todos os resultados, e é lido com o "é do tipo", e é seguido por uma assinatura de tipo, ou anotação de tipo.
:t nos mostra que valores de verdadeiro (True) ou falso (False) são do tipo Bool. Até mesmo expressões possuem um tipo definido: quando elas forem avaliadas, elas resultarão num valor do tipo Bool. Talvez já esteja claro para você, mas vale notar que Bool é o tipo de dado escolhido para representar valores do tipo sim ou não. Por exemplo: se um valor for encontrado ou não, se um botão foi pressionado ou não.

## Caracteres e strings

Vamos agora testar :t com outros coisas. Em Haskell, um único caractere é representado cercando-o por aspas simples:
Temos, então, que caracteres são do tipo Char, abreviação de character (caractere, em inglês). Agora, em vez de usarmos um único caractere, para representar uma cadeia deles, devemo usar aspas duplas:
De novo, temos Char, só que cercado por colchetes. [Char] significa uma lista de caracteres. Haskell considera que toda cadeia de caracteres é uma lista. Lista, em geral, são bem importantes em Haskell, e falaremos sobre elas mais adiante.

### Exercício

* Use :type seguido de um valor como "H" no GHCi. Perceba que usamos aspas duplas aqui. O que acontece? Por quê?
* Use :type seguido de um valor como 'Olá, mundo' no GHCi. Perceba que usamos aspas simples aqui. O que acontece? Por quê?
Haskell nos permite criar sinônimos de tipos, que funcionam como sinônimos na linguagem humana: palavras diferentes que tem o mesmo significado. Esses sinônimos são nomes alternativos para os tipos. Por exemplo, String é definido como um sinônimo para [Char], e por isso podemos substituir um pelo outro:
O código acima é perfeitamente válido, e é muito mais claro para humanos ler String uma única vez, do que ler [Char] e interpretar que trata-se de uma lista de caracteres e, portanto um string.

## Tipos das funções

Até agora vimos os tipos de alguns valores (strings, booleanos, caracteres etc.). Também vimos que tais tipos nos ajudam a categorizar e descrever certos valores de forma mais convenientes. Agora, veremos o que faz o sistema de tipos de Haskell tão importante e poderoso: funções possuem tipos também.

### Exemplo

Podemo inverter um booleano usando a função not, como já vimos. Para sabermos o tipo de uma função, temos levar duas coisas em consideração: os tipos de seus argumentos, e o tipo do valor que ela retorna, isto é, o tipo de sua saída. Para o caso de not as coisas são bem simples: ela recebe um valor do tipo Bool e retorna outro do tipo Bool. A notação que usamos em Haskell é a seguinte:
Essa linha pode ser lida como: "not é do tipo que recebe valores do tipo Bool e retorna valores do tipo Bool". Em outras palavras: uma função de Bool (a entrada) em Bool (a saída).
No GHCi, podemos usar :t em funções também:
Portanto, o tipo de uma função sempre é descrito em termos dos tipos de sua entrada e do tipo de sua saída.

### Exemplo: chr e ord

A representação de texto sempre foi um problema para computadores. Em sua linguagem mais básica, um computador só pode interpretar dados binários: 1 e 0. Para representar um texto, cada caractere deve primeiramente ser convertido a um número, que pode ser convertido para sua forma binária e depois interpretada pelo computador. A conversão para a forma binária é uma tarefa trivial para a máquina, portanto, só nos interessa a conversar de um caractere para sua forma numérica.
A forma mais simples para tal tarefa seria escrever todos os caracteres possíveis e enumerá-los. Por exemplos, se fizermos "a" corresponder a 1, "b" poderia ser 2, "c" seria 3, e assim por diante. Haskell já possui um padrão de enumeração de caracteres definido, e seria tedioso buscar em sua tabela os valores numéricos sempre que precisarmos deles. Por conveniência, já temos duas funções que fazem este trabalho: chr (uma abreviação de char), e ord. Vamos investigar os tipos de chr e ord:
Como já sabemos o que Char significa, vamos explicar sobre Int. Este tipo se refere a números inteiros, e é apenas um dentre alguns outros tipos de números definidos em Haskell. Na verdade, existe também mais de um tipo para representar números inteiros em Haskell. A assinatura de tipo de chr nos diz que ela recebe um argumento do tipo Int, um número inteiro, e avalia um resultado do tipo Char. O caso de ord é o oposto: recebe um argumento do tipo Char e retorna um do tipo Int. Analisando ambas assinaturas, fica claro qual das funções converte um caractere num número (ord), e qual faz a operação inversa (chr).
Agora, alguns exemplos. Estas funções não estão nativamente disponíveis, ao abrir uma sessão do GHCi, precisamos usar o comando :module Data.Char (ou usar :m) para carregar o módulo Data.Char, que é onde elas estão definidas.

### Funções de mais de um argumento

Qual seria o tipo de uma função com mais de um argumento?
xor é a função "ou exclusivo", que avalia se apenas um dos argumentos for True. Se ambos ou nenhum forem verdadeiros, ela retorna False.
O método básico para descrever o tipo de uma função de mais de um argumento é simplesmente escrever os tipos de cada argumento, em linha, e em sequência (primeiro p, depois q, por exemplo) e, por último, adicionar o tipo da saída. Depois basta intercalá-los com ->.
* Escreva o tipo dos argumentos de entrada, em sequência, e o tipo da saída no fim. A definição de xor já nos diz que ela avalia se seus argumentos são True ou False. Então eles só podem ser do tipo Bool. Sua saída também é do mesmo tipo, Bool: Bool Bool Bool ^^^^ p é do tipo Bool ^^^^ q é do tipo Bool ^^^^ a saída também é do tipo Bool
* Intercale os tipos usando ->: Bool -> Bool -> Bool
Portanto, a assinatura de xor é:
Ela pode ser lida como: "xor é do tipo que recebe dois Bool e retorna um Bool".

### Exemplo: abrirJanela

A medida que alguém aprofunda seus conhecimentos de programação, alguns termos técnicos tornam-se triviais. Um deles, são as chamadas bibliotecas, que são arquivos de código com um determinado propósito, mas que podem ser usados por diferentes programas. E um dos tipos de bibliotecas mais comuns é o de GUI (Graphical Unser Interface, ou interface gráfica de usuário, em inglês). O que se espera encontrar neste tipo de biblioteca são funções para criar, botões, menus e interação com mouse e teclado, por exemplo. Uma possível função seria abrirJanela, que provavelmente abre uma nova janela quando executada. A assinatura de uma função como esta poderia ser:
Não precisamos entender a fundo a definição destes tipos, mas seus nomes nos ajudam a entender o que é necessário para criar uma janela. É bastante provável também que eles estejam definidos dentro da biblioteca da própria biblioteca que possui define abrirJanela.
Vamos investigar por partes. Lembre-se do que explicamos na seção anterior: o último tipo descrito na assinatura de uma função é o tipo de sua saída. Portanto, quantos argumentos abrirJanela exige? Dois argumentos. Quais são os seus tipos? JanelaTitulo e JanelaTamanho. E qual o tipo da saída? É Janela. Assim, podemos entender que: "abrirJanela é uma função que cria uma nova janela e que para isso exige um título e um tamanho".
Perceba que não é preciso já ter usado uma função para entender o que ela faz: quando usamos tipos cujos nomes são claros o bastante, a assinatura da função nos dá dicas de seu funcionamento. É um bom hábito testar as funções com :t no GHCi antes de usá-la e, quanto mais você o fizer, melhor será sua intuição para interpretar os tipos das funções.
* Exercícios
* Tente deduzir quais as anotações de tipos das seguintes funções. Se alguma delas envolver números, suponha que sejam do tipo Int.
* negativo: uma função que recebe um Int e retorna seu oposto (sinal invertido). Ex: negativo 4 = -4; negativo (-2) = 2.
* (||): a função lógica "ou". Ela recebe dois booleanos e compara se pelo menos um deles é verdadeiro. Se sim, retorna verdadeiro, se não, falso.
* diasNoMes: uma função que calcula a quantidade de dias num determinado mês. Ela recebe uma valor para indicar se o ano é bissexto ou não, e o número do mês, de 1 a 12.
* f x y = not x || y
* g x = (2*x - 1)^2

## Anotações de tipo em arquivos de código

Agora que já entendemos um pouco o que são os tipos e no que eles podem nos ajudar, vejamos como fazer este tipo de anotação num código fonte de um programa que você está escrevendo. Considere a função xor que definimos antes. Se quisermos defini-la dentro de um arquivo, e quisermos anotar seu tipo, basta escrever:
Só isso. Para maior clareza, é comum que a anotação apareça imediatamente antes da definição, então é recomendável que você se atenha a esta convenção.
Além de facilitar que humanos entendam o funcionamento da função, as anotações de tipo tão são fundamentais para que o compilador as interprete corretamente, como veremos a seguir.

### Inferindo tipos

Como dissemos antes, funções possuem tipo. É importante destacar que elas sempre possuem tipo. Mas como é que escrevemos as funções do capítulo anterior sem nenhum assinatura? Isso é possível porque o compilador consegue inferir o tipo de uma função baseando-se nas operações que ela executa. Vejamos o seguinte caso:
ehL é uma função de um argumento e que retorna o resultado da expressão c == 'l'. Mesmo sem anotarmos explicitamente o tipo de c e da saída, podemos tentar deduzir a assinatura de ehL. Veja:
* É uma função de um argumento, c. E toda função em Haskell tem apenas uma saída.
* A saída é resultado da operação c == 'l'. Como já vimos, (==) é uma comparação de igualdade, e comparações geralmente resultam em respostas do tipo sim ou não, o que pode ser representado pelo tipo Bool. Na verdade, já vimos (==) em Verdadeiro ou falso, então sabemos que sua saída é um Bool, com certeza.
* A função (==) compara c com 'l'. Sabemos que não faz sentido comparar coisas de tipos diferentes. O valor 'l' é do tipo Char, portanto, só faz sentido que c seja do mesmo tipo.
Se quiser verificar o que acabamos de fazer, defina ehL no GHCi sem uma assinatura e depois use :t para verificar seu tipo:
O compilador realiza um processo bastante parecido com este para definir o tipo de uma função. Entretanto, ele pode não resultar na assinatura que você espera ou deseja, ou sequer conseguir chegar a uma assinatura final. Por isso, é recomendável sempre escrever a anotação de tipo de suas funções:
Há também outros bons motivos para incluí-las no código:
* Documentação: como já vimos, anotações de tipo ajudam a entender o que uma função faz. Documentar um programa, é justamente descrever o que suas partes fazem e como funcionam. Claro que comentários ajudam, mas em Haskell as anotações de tipo são tão importantes quanto comentários.
* Debugging: muitos bugs, ou erros encontrados nos programas são causados quando se uma função de forma errada. As anotações de tipo ajudam você e o compilador a descobrir se uma função está sendo usada de forma correta, verificando os tipos das muitas entradas e saídas que seu programa pode gerar. Quando declaramos as anotações e ocorre algum erro de incompatibilidade de tipos, o compilador sempre mostra uma mensagem dizendo onde está o problema. Se os tipos forem omitidos, o compilador pode inferi-las de uma forma que funcionem num parte, mas que causem outros erros inesperados durante a execução, ou outro erro de compilação em outra parte.
Vejamos um exemplo simples de debugging. Tente o seguinte no GHCi:
Acabamos de introduzir a função (++), que nada mais faz que concatenar os dois Strings: "ola" e " mundo". Se por um erro de digitação você escrever + em vez de ++, a mensagem de erro acima apareceria. De forma resumida, ela nos diz que (+) só está definida para dados Num, e que estamos tentando aplicá-la a dados do tipo [Char], ou String, como já vimos.
É importante dizer que a checagem de tipos não previne completamente a ocorrência de erros. Na verdade, ainda podemos escrever uma função de uma forma que não funcione como a desejada. Mas mesmo aí, pensar nas operações que podemos ou não fazer como cada tipo de dado, nos ajuda guiar nossa lógica programação.

## Notas

* ↑ Não é exatemente assim que chr e ord funcionam, mas descrição é válida por enquanto.
* ↑ Cobriremos mais sobre este assunto no capítulo sobre funções de alta ordem.