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 -> String
que é "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 .
a -> String
. Você provavelmente teria uma restrição de tipo, comoShow a => a -> String
.