Haskell/Declaração de tipos: diferenças entre revisões

[edição verificada][revisão pendente]
Conteúdo apagado Conteúdo adicionado
mSem resumo de edição
m <source> -> <syntaxhighlight> (phab:T237267)
 
Linha 27:
Agora que já definimos o que queremos armazenar, podemos definir o tipo <tt>Aniversario</tt> usando <tt>data</tt>:
 
<sourcesyntaxhighlight lang="haskell">
data Aniversario = Nascimento String Int Int Int -- nome, ano, mês, dia
| Casamento String String Int Int Int -- nome 1, nome 2, ano, mês, dia
</syntaxhighlight>
</source>
 
O tipo <tt>Aniversario</tt> possui dois ''construtores de tipo'': <tt>Nascimento</tt> e <tt>Casamento</tt>, sendo que cada um deles exige certos valores, como já vimos no nosso esboço. Estes construtores também pode ser chamados de ''funções de construção'' ou ''funções construtoras''.
Linha 46:
 
Já que podemos tratar construtores de tipos como funções, podemos usá-los como funções normais. Por exemplo, para armazenar o aniversário de João Romão, nascido no dia 3 de julho de 1968:
<sourcesyntaxhighlight lang="haskell">
joaoRomao :: Aniversario
joaoRomao = Nascimento "João Romão" 1968 7 3
</syntaxhighlight>
</source>
 
Ele se casou com Maria Romão no dia 4 de março de 1987:
<sourcesyntaxhighlight lang="haskell">
romaoCasamento :: Aniversario
romaoCasamento = Casamento "João Romão" "Maria Romão" 1987 3 4
</syntaxhighlight>
</source>
 
Apesar de armazenarem dados diferentes, <code>joaoRomao</code> e <code>romaoCasamento</code> são ambos do tipo <code>Aniversario</code>. Isso quer dizer que podemos armazená-los juntos numa única lista:
<sourcesyntaxhighlight lang="haskell">
aniversariosDeJoaoRomao :: [Aniversario]
aniversariosDeJoaoRomao = [joaoRomao, romaoCasamento]
</syntaxhighlight>
</source>
 
Também poderíamos ter criado a lista sem variáveis intermediárias, mas o código poderia não ficar tão legível:
<sourcesyntaxhighlight lang="haskell">
aniversariosDeJoaoRomao :: [Aniversario]
aniversariosDeJoaoRomao = [Nascimento "João Romão" 1968 7 3, Casamento "João Romão" "Maria Romão" 1987 3 4]
</syntaxhighlight>
</source>
 
É importante saber que cada novo tipo declarado com a <tt>data</tt> deve possuir pelo menos um construtor. Nestes casos, você também verá que é conveniente que o construtor tenha mesmo nome que o tipo:
<sourcesyntaxhighlight lang="haskell">
data Dado1 = Dado1 -- Dado1 possui apenas um construtor e não recebe nenhum valor adicional
data Dado2 = Dado2 Int -- Dado2 possui apenas um construtor e recebe um valor adicional do tipo Int
</syntaxhighlight>
</source>
 
== Acessando valores armazenados ==
Linha 80:
 
Para nosso exemplo acima, uma operação muito útil seria extrair os nomes e as datas armazenadas, e poder apresentá-los em forma de String. Para isso, criamos uma função chamada <code>mostrarAniversario</code>. Como temos que converter os valores numéricos das datas em String, podemos criar também a função auxiliar <code>mostrarData</code> que usa <code>show</code> para converter os números:
<sourcesyntaxhighlight lang="haskell">
mostrarData :: Int -> Int -> Int -> String
mostrarData a m d = show a ++ "-" ++ show m ++ "-" ++ show d
Linha 89:
mostrarAniversario (Casamento nome1 nome2 ano mes dia) =
nome1 ++ " casou-se com " ++ nome2 ++ " em " ++ mostrarData ano mes dia
</syntaxhighlight>
</source>
 
Usamos casamento de padrões para criar <code>mostrarAniversario</code>. São dois padrões: um deles casa com dados construídos com <code>Nascimento</code>, o outro casa com dados construídos com <code>Casamento</code>. Depois de definir o construtor, definimos as ''variávies de vínculo'' (''binding variables'', em inglês). Estes são os nomes que damos para cada valor contido no dado, como <code>nome</code>, <code>ano</code> ou <code>mes</code>, por exemplo. Poderíamos ter escrito <code>Nascimento n a m d</code> e depois usado <code>n</code>, <code>a</code>, <code>m</code>, e <code>d</code> para nos referirmos a cada valor ao longo da definição da função, mas usar nomes mais expressivos deixa o código mais claro.
Linha 113:
 
Como dito no começo deste capítulo, ter mais clareza é um dos objetivos que nos leva a criar novos tipos. Neste sentido, seria bom poder deixar claro que os String em <tt>Aniversario</tt> são nomes e ainda poder manipulá-los como um String comum. Para isso, podemos usar a palavra-chave <tt>type</tt> para criar um sinônimo:
<sourcesyntaxhighlight lang="haskell">
type = Nome = String
</syntaxhighlight>
</source>
 
Esta linha quer dizer que <tt>Nome</tt> agora é um sinônimo de <code>String</code>. Isso quer dizer que qualquer função que aceite um dado String também aceitará um do tipo <tt>Nome</tt>, e vice-versa. O lado direito da expressão também pode ser um tipo mais complexo. O próprio tipo String é sinônimo de uma lista de Char, e podemos confirmar isso usando o comando <code>:info</code> (ou <code>:i</code>) no GHCi para obter informações sobre os tipos e outras funções:
Linha 123:
 
<tt>GHC.Base</tt> é onde está definido o Prelude. Podemos fazer algo semelhante para uma lista de aniversários:
<sourcesyntaxhighlight lang="haskell">
type LivroDeAniversarios = [Aniversario]
</syntaxhighlight>
</source>
 
Sinônimos são, em geral, apenas uma conveniência. Eles geralmente nos ajudam a esclarecer o papel tipos complexos -- como listas, n-uplas ou funções -- dentro de um certo contexto. Entretanto, deve-se evitar o uso em excesso dos sinônimos, pois o código pode se tornar bastante confuso: um código que usa vários sinônimos diferentes para um mesmo tipo pode dificultar o seu entendimento.
Linha 136:
O uso de <tt>data</tt> ainda nos permite definir tipos como sendo registros. Por exemplo, o tipo <tt>Data</tt> definido como registro seria:
 
<sourcesyntaxhighlight lang="haskell">
data Data = Data {ano :: Int, mes :: Int, dia :: Int}
</syntaxhighlight>
</source>
 
Para definir os valores de cada campo, usamos a mesma sintaxe, sendo que a ordem dos campos não importa:
 
<sourcesyntaxhighlight lang="haskell">
fimDoSeculoXIX = Data 1900 12 31
fimDoSeculoXXI = Data {dia = 31, mes = 12, ano = 2100}
</syntaxhighlight>
</source>
 
Esta definição cria, também, três ''funções de acesso'': <code>ano</code>, <code>mes</code> e <code>dia</code>. Podemos ver seus tipos no GHCi:
Linha 162:
Esta sintaxe também ajuda bastante quando precisamos mudar os valores de alguns campos, em vez de todos:
 
<sourcesyntaxhighlight lang="haskell">
fimDoSeculoXX = fimDoSeculoXXI {ano = 2000}
</syntaxhighlight>
</source>
 
Registros são especialmente úteis quando precisamos criar variáveis que armazenem muitos valores. Por exemplo, sem usar a sintaxe de registros, uma agenda de contatos — onde usamos Int para representar números de telefone — poderia ser:
 
<sourcesyntaxhighlight lang="haskell">
data Contato = Contato
String -- nome
Linha 176:
String -- email
type Agenda = [Contato]
</syntaxhighlight>
</source>
 
O principal problema de <code>Cotato</code> é que sua definição pode causar confusão sobre o que cada valor deve ser. Usar <code>:i</code> no GHCi não ajuda muito:
Linha 187:
Se o tipo <code>Contato</code> fosse criado como um registro, uma vez que damos nomes para cada campo, esse mesmo problema não existiria, pois saberíamos exatamente o que campo deve ser:
 
<sourcesyntaxhighlight lang="haskell">
data Contato = Contato { nome :: String
, sobrenome :: String
Linha 194:
, email :: String
}
</syntaxhighlight>
</source>
 
O GHCi também seria útil neste caso:
Linha 210:
Devemos ter cuidado, entretanto, para não criarmos campos de tipos diferentes com mesmo nome dentro de um mesmo programa.<ref group=nota>Mais especificamente, dentro de um mesmo módulo.</ref> Por exemplo, o compilador não aceitaria as seguintes definições, retornando um erro sobre múltiplas declarações de <code>campo1</code>:
 
<sourcesyntaxhighlight lang="haskell">
data Dado1 = Dado1 {campo1 :: Int}
data Dado2 = Dado2 {campo1 :: Int, campo2 :: String}
</syntaxhighlight>
</source>
 
{{Exercícios|1=