Os tipos são apagados no Haskell?

13

Haskell tem uma noção de "funções genéricas" que tem alguma aparente semelhança com o lisp comum - não tendo experiência com Haskell nem com lisp comum, talvez eu seja muito aproximado aqui. Isso significa que é possível definir um to_stringrecurso genérico para definir uma representação de string para todos os tipos. Obviamente, a instalação deve ser definida em casos especiais, mas existe uma to_stringfunção cuja assinatura é α → string.

Os tipos são apagados no Haskell, assim como no OCaml? Se sim, como a implementação de “funções genéricas” em Haskell difere da do lisp comum, onde os tipos são dinâmicos e, portanto, não são apagados?

Entendo que os detalhes da implementação são específicos do compilador, mas provavelmente existem disposições comuns a muitas ou todas as implementações.

user40989
fonte
5
Lembre-se de que você provavelmente não pode definir uma função não trivial do tipo a -> String. Você provavelmente teria uma restrição de tipo, como Show a => a -> String.
DJG

Respostas:

16

A resposta até agora é enganosa.

É necessário fazer uma distinção entre polimorfismo "paramétrico" e "sobrecarga ad-hoc". Os parâmetros paramétricos "se comportam de maneira uniforme para todos os tipos a", enquanto "ad-hoc" - o que Simon chama de polimórfico - altera a implementação com base no tipo.

Exemplos de ambos são reverse :: [a] -> [a], que é paramétrico e show :: Show a => a -> Stringque é "ad-hoc" sobrecarregado.

Se você deseja mais uma intuição abstrata, acho que ajuda considerar as classes de verbos da linguagem natural que 'funcionam' para todos os objetos, como "possuir" ou "pensar em" que não apresentam restrições ao objeto, mas " abrir "requer que o que estamos falando possa ser aberto. Consigo "pensar em uma porta" e "abrir uma porta", enquanto não faz sentido, por exemplo, "abrir uma árvore". Para levar o exemplo ainda mais longe, "abrir" é "polimórfico ad-hoc" como "abrir uma janela" e "abrir um registro de reclamação com o atendimento ao cliente" são duas coisas muito diferentes. Se isso parece forçado - esqueça! Funciona para mim.

Ambos são resolvidos em tempo de compilação, no entanto, e de fato "apagados". Módulo várias extensões de GHC e Template Haskell, etc. De fato, os tipos são apagados no tempo de compilação e nunca são inspecionados no tempo de execução.

As funções parametricamente polimórficas se comportam de forma idêntica para todos os tipos, portanto, apenas um pedaço de código precisa ser gerado, enquanto o compilador decide em tempo de compilação qual versão de uma função "classificada como tipo" precisa ser executada em um ponto específico do programa. É também por isso que existe a restrição de uma instância por tipo por classe de tipo e a solução alternativa "newtype" correspondente.

A implementação está detalhada no livro didático de SPJs e no artigo Wadler e Blotts sobre classes de tipo .

Kris
fonte
Você acha que eu deveria excluir minha resposta?
Simon Bergot
Não, está tudo bem com o seu aviso de não bater o vocabulário exato:)
Kris
Você poderia expandir um pouco o motivo pelo qual o GHC pode apagar tipos? É graças à digitação principal?
Simon Bergot
2
Em geral, em tempo de execução, as funções parametricamente polimórficas podem existir apenas uma vez e chamar as funções ad-hoc polimórficas por meio de algum método de despacho virtual ou todo o código pode ser gerado para cada tipo separadamente e as chamadas vinculadas estaticamente. Qualquer implementação está correta do ponto de vista do idioma (observe que Haskell não possui tipos polimórficos; isso só pode ser criado com a forallextensão GHC ).
Jan Hudec
Existe alguma extensão do GHC que não permita que tipos sejam apagados? Sobrecarga é estática e sem tipos dependentes completos eu não consigo pensar em nada
Daniel Gratzer
1

aviso: minha formação em CS não é muito forte, por isso nem sempre posso usar o vocabulário correto e posso estar errado em alguns pontos. Enfim, aqui está o meu entendimento:

Eu acho que você está confundindo genéricos com polimorfismo.

Tipos genéricos como List<Foo>são usados ​​para descrever tipos que usam outros tipos como parâmetro e permitem uma verificação mais rica do tipo.

Uma função genérica no haskell pode ser count:: List a -> Int. Ele aceita listas de qualquer tipo e retorna o número de elementos. Existe apenas uma implementação.

Geralmente, ao definir Lista, você não pode assumir nada sobre T. Você só pode armazená-lo e devolvê-lo.

Sua to_stringfunção é uma função polimórfica, o que significa que se comportará de maneira diferente em diferentes circunstâncias. Em haskell, isso é feito com classes de tipos, que funcionam como adjetivos para tipos.

Você tem uma Showclasse de tipo, que define uma show :: a -> Stringfunção.

Quando um tipo Fooimplementa a Showclasse, deve fornecer a definição de show. Esse tipo pode ser usado em funções que exigem o Showqualificador (como em Show a => a -> whatever). Haskell não pode apagar tipos, pois essas funções precisam encontrar a implementação correta da showfunção.

Simon Bergot
fonte
Em linguagem comum, funções polimórficas são chamadas de "funções genéricas" IIRC e eu usei o mesmo vocabulário (confuso). Sua resposta é útil e seu argumento final bastante convincente. ()
user40989
“Existe apenas uma implementação” - apenas uma que faz sentido, com certeza, mas existem infinitamente muitas possíveis. Uma função do tipo a → b possui | b | ^ | a | possíveis implementações (considere o número de funções do tipo Bool → Bool) e as listas não têm limite de tamanho superior.
Jon Purdy