# Haskell/Módulos

* Módulos
* Intermediário
* Módulos
* Programas completos
* Indentação
* Mais tipos
* Estruturas de controle
* Classes e tipos
* A classe Functor
Módulos são a principal forma de organizar código em Haskell. Nós os conhecemos quando declaramos import para inserir uma biblioteca de funções em nosso código. Além de nos permitir fazer melhor uso de bibliotecas, conhecer os módulos irá nos ajudar a construir nossos próprios programas e criar programas completos, os quais podem ser executados independente do GHCi (aliás, esse é o tópico do próximo capítulo, Programas completos).

## Módulos

Os Módulos do Haskell são uma maneira útil de agrupar um conjunto de funções com funcionalidade relacionadas em um único pacote e gerenciar diferentes funções com mesmo nomes.
As declarações e importações de módulos devem ser escritas no início dos seus arquivos Haskell.
A definição básica de módulos é deste modelo:
Note que:
* O nome do módulo inicia com letra maiúscula;
* Cada arquivo deve conter apenas um módulo.
O nome do arquivo é o nome do módulo mais a extensão .hs. Qualquer ponto '.' no nome do módulo é alterado por diretórios. Então, o módulo SeuModulo estará no arquivo SeuModulo.hs, enquanto o módulo MeuDiretorio.MeuModulo estará no arquivo MeuDiretorio/MeuModulo. Como o nome do módulo inicia com uma letra maiúscula, o nome do arquivo também deve iniciar.

## importação

Os módulos podem importar funções de outros módulos. Isto é, entre a declaração do módulo e o resto do código você pode adicionar declarações import, desta forma:
datatypes importados são especificados pelos seus nomes, seguidos por uma lista de seus construtores importados, essa lista deve estar entre parêntesis, veja:
E se importarmos alguns módulos com definição sobreposta? E se importarmos um módulo, mas quisermos sobrescrever algumas de suas funções? Há três caminhos para lidar com esses casos: Importação qualificada, escondendo definição e renomeando importações.

### Importação qualificada

Nos códigos abaixo, MeuModulo e MeuOutroModulo ambos possuem uma definição para remove_e, que remove todas as letras e da uma string. No entanto, MeuModulo remove apenas e minúsculos, e MeuOutroModulo remove ambos, minúsculos e maiúsculos. Nesse caso, o seguinte código é ambíguo:
Não fica claro o que remove_e significa. Para evitar isso, é usado a palavra-chave qualified:
No último trecho de código, não há uma função disponível com o nome remove_e. Quando fazemos importação qualificada todos os valores e funções importados incluem o nome do módulo como prefixo. Aliás, você também pode incluir o prefixo mesmo fazendo a importação comum (sem a palavra-chave qualified inclusa).

### Escondendo definição

Agora, suponha que queremos importar ambos, MeuModulo e MeuOutroModulo, mas sabemos que queremos remover todos os e's, não apenas os minúsculos. Ficará muito tedioso adicionar MeuOutroModulo toda vez que chamar remove_e. Não podemos apenas excluir o remove_e do MeuModulo?
Isso funciona por causa da palavra hiding. O que vier em seguida dessa palavra-chave não será importado. Esconde-se múltiplos itens listando-os entre parêntesis e separando-os por virgula.
Note que datatypes e tipos sinônimos não podem ser removidos. Eles são sempre importados. Se você tem um datatype definido em múltiplos arquivos, você deve usar nomes qualificados.

### Renomeando importações

Imagine que você tem um módulo com um nome longo, como este:
Especialmente quando usamos importação qualificada, isto é irritante. Podemos melhorar isso usando a palavra-chave as:
Isso nos permite usar Pequeno ao invés de usar MeuModuloComUmNomeMuitoGrande como prefixo para as funções importadas. Essa renomeação funciona para importações comuns e qualificadas.
Desde que não haja itens conflitantes, você pode importar múltiplos módulos e renomeá-los para o mesmo nome:
Nesse caso ambas as funções em MeuModulo e em MeuModuloComUmNomeMuitoGrande podem ser prefixadas com Meu.

### Combinando renomeação com importação limitadas

Às vezes, é conveniente usar diretiva de importação duas vezes para o mesmo módulo. Um cenário típico é o seguinte:
Isso dá acesso a todos os módulos de Data.Set pelo apelido Set, e também as funções (empty, insert, e o construtor Set sem usar o prefixo Set.

## Exportando

No exemplo do início deste artigo, é falado "importa todas as funções exportadas do módulo". Isto levanta uma questão, como decidimos quais funções podem ser exportadas e quais permanecem internas? Desta forma:
Nesse caso, somente remove_e e add_two são exportadas. Enquanto add_two é permitido usar add_one, funções em módulos que importarão MeuModulo não podem usar add_one diretamente, pois, não foi importada.
Exportar tipo de dados é semelhante as suas importação. Você digita o nome do tipo e uma lista de construtores entre parêntesis:
Nesse caso, a declaração do módulo pode ser reescrita "MeuModulo2 (Arvore(..))", declarando que todos os construtores são exportados.
Manter uma lista de exportação é uma boa prática não somente porque reduz a poluição causada por a exportação de funções que não são utilizadas, como também habilita certas otimizações que em outros casos não são possíveis.

## Notas

* ↑ Veja adocumentação original para mais detalhes.
* ↑ No haskell 98, a última versão padronizada antes do Haskell 2010, o sistema de módulos era bastante conservador, mas a prática comum recente consiste em empregar um sistema hierárquico de módulos, usando períodos para separar os espaços para nome.
* ↑ É falado sobre construtores de tipos e dados em Declaração de Tipos
* ↑ Há uma ambiguidade entre um nome qualificado (como, MeuModulo.remove_e) e o operador de composição (.). Escrever reverse.MeuModulo.remove_e provavelmente confunde seu compilador Haskell. Uma solução é: sempre use espaços entre este operador, por exemplo reverse . MeuModulo.remove_e
* ↑ Um módulo pode exportar funções que ele importa. Recursão mútua é possível, mas, precisa de uma atenção especial.