Enquanto estou aprendendo Haskell, notei sua classe de tipo , que é considerada uma grande invenção originada de Haskell.
No entanto, na página da Wikipedia sobre classe de tipo :
O programador define uma classe de tipo especificando um conjunto de nomes de funções ou constantes, junto com seus respectivos tipos, que devem existir para cada tipo que pertence à classe.
O que me parece bastante semelhante à interface do Java (citando a página Interface (Java) da Wikipedia ):
Uma interface na linguagem de programação Java é um tipo abstrato usado para especificar uma interface (no sentido genérico do termo) que as classes devem implementar.
Esses dois parecem bastante semelhantes: a classe de tipo limita o comportamento de um tipo, enquanto a interface limita o comportamento de uma classe.
Eu me pergunto quais são as diferenças e semelhanças entre a classe de tipo em Haskell e a interface em Java, ou talvez sejam fundamentalmente diferentes?
EDIT: Eu percebi que até haskell.org admite que eles são semelhantes . Se eles são tão semelhantes (ou são?), Então por que a classe de tipos é tratada com tanto exagero?
MAIS EDITAR: Uau, tantas respostas ótimas! Acho que vou ter que deixar a comunidade decidir qual é o melhor. No entanto, ao ler as respostas, todos eles parecem apenas dizer que "há muitas coisas que o typeclass pode fazer enquanto a interface não pode ou tem que lidar com os genéricos" . Não posso deixar de me perguntar: há algo que as interfaces podem fazer enquanto as typeclasses não podem? Além disso, percebi que a Wikipedia afirma que a typeclass foi originalmente inventada no artigo de 1989 * "Como tornar o polimorfismo ad-hoc menos ad hoc", enquanto Haskell ainda está em seu berço, enquanto o projeto Java foi iniciado em 1991 e lançado pela primeira vez em 1995 Então, talvez em vez de a typeclass ser semelhante às interfaces, seja o contrário, as interfaces foram influenciadas pela typeclass?Existem documentos / papéis que apóiam ou desmentem isso? Obrigado por todas as respostas, todas são muito esclarecedoras!
Obrigado por todas as entradas!
Respostas:
Eu diria que uma interface é como uma classe de tipo
SomeInterface t
onde todos os valores têm o tipot -> whatever
(ondewhatever
não contémt
). Isso ocorre porque, com o tipo de relacionamento de herança em Java e linguagens semelhantes, o método chamado depende do tipo de objeto no qual são chamados e nada mais.Isso significa que é muito difícil fazer coisas como
add :: t -> t -> t
uma interface, onde é polimórfica em mais de um parâmetro, porque não há como a interface especificar que o tipo de argumento e o tipo de retorno do método são do mesmo tipo que o tipo de o objeto que é chamado (ou seja, o tipo "self"). Com os Genéricos, existem maneiras de fingir isso criando uma interface com parâmetro genérico que deve ser do mesmo tipo que o próprio objeto, como comoComparable<T>
faz, onde se espera que você useFoo implements Comparable<Foo>
para que ocompareTo(T otherobject)
tipo tenha tipot -> t -> Ordering
. Mas isso ainda requer que o programador siga esta regra, e também causa dor de cabeça quando as pessoas querem fazer uma função que usa essa interface, elas têm que ter parâmetros de tipo genérico recursivo.Além disso, você não terá coisas como
empty :: t
porque não está chamando uma função aqui, portanto, não é um método.fonte
O que é semelhante entre interfaces e classes de tipo é que elas nomeiam e descrevem um conjunto de operações relacionadas. As próprias operações são descritas por meio de seus nomes, entradas e saídas. Da mesma forma, pode haver muitas implementações dessas operações que provavelmente serão diferentes em sua implementação.
Com isso resolvido, aqui estão algumas diferenças notáveis:
Em geral, acho que é justo dizer que as classes de tipo são mais poderosas e flexíveis do que as interfaces. Como você definiria uma interface para converter uma string em algum valor ou instância do tipo de implementação? Certamente não é impossível, mas o resultado não seria intuitivo ou elegante. Você já desejou que fosse possível implementar uma interface para um tipo em alguma biblioteca compilada? Ambos são fáceis de realizar com classes de tipo.
fonte
As classes de tipo foram criadas como uma forma estruturada de expressar "polimorfismo ad-hoc", que é basicamente o termo técnico para funções sobrecarregadas . Uma definição de classe de tipo se parece com isto:
O que isso significa é que, quando você usa aplica a função
foo
a alguns argumentos de um tipo que pertencem à classeFoobar
, ela procura uma implementaçãofoo
específica para aquele tipo e a usa. Isso é muito semelhante à situação com sobrecarga de operador em linguagens como C ++ / C #, exceto mais flexível e generalizado.As interfaces têm um propósito semelhante em linguagens OO, mas o conceito subjacente é um pouco diferente; As linguagens OO vêm com uma noção embutida de hierarquias de tipo que Haskell simplesmente não tem, o que complica as coisas de algumas maneiras porque as interfaces podem envolver tanto sobrecarga por subtipagem (ou seja, métodos de chamada em instâncias apropriadas, subtipos que implementam interfaces de seus supertipos) e por despacho baseado em tipo simples (já que duas classes que implementam uma interface podem não ter uma superclasse comum que também a implementa). Dada a enorme complexidade adicional introduzida pela subtipagem, sugiro que seja mais útil pensar nas classes de tipo como uma versão aprimorada de funções sobrecarregadas em uma linguagem não OO.
Também vale a pena notar que as classes de tipo têm meios muito mais flexíveis de despacho - as interfaces geralmente se aplicam apenas à única classe que as implementa, enquanto as classes de tipo são definidas para um tipo , que pode aparecer em qualquer lugar na assinatura das funções da classe. O equivalente a isso em interfaces OO seria permitir que a interface defina maneiras de passar um objeto dessa classe para outras classes, definir métodos estáticos e construtores que selecionariam uma implementação com base em que tipo de retorno é necessário no contexto de chamada, definir métodos que pegar argumentos do mesmo tipo que a classe que implementa a interface e várias outras coisas que realmente não se traduzem.
Resumindo: eles servem a propósitos semelhantes, mas a maneira como funcionam é um pouco diferente, e as classes de tipo são significativamente mais expressivas e, em alguns casos, mais simples de usar por trabalharem em tipos fixos em vez de partes de uma hierarquia de herança.
fonte
Eu li as respostas acima. Sinto que posso responder um pouco mais claramente:
Uma "classe de tipo" Haskell e uma "interface" Java / C # ou um "traço" Scala são basicamente análogos. Não há distinção conceitual entre eles, mas existem diferenças de implementação:
fonte
Em Master Minds of Programming , há uma entrevista sobre Haskell com Phil Wadler, o inventor das classes de tipo, que explica as semelhanças entre interfaces em Java e classes de tipo em Haskell:
Portanto, classes de tipo estão relacionadas a interfaces, mas a correspondência real seria um método estático parametrizado com um tipo como acima.
fonte
Assista à palestra de Phillip Wadler Faith, Evolution, and Programming Languages . Wadler trabalhou em Haskell e foi um grande contribuidor para Java Generics.
fonte
Leia Extensão de software e integração com classes de tipo, onde são fornecidos exemplos de como as classes de tipo podem resolver uma série de problemas que as interfaces não podem.
Os exemplos listados no artigo são:
fonte
Eu não posso falar com o nível de "hype", se parece que sim. Mas sim, as classes de tipo são semelhantes de várias maneiras. Uma diferença que posso pensar é que Haskell você pode fornecer comportamento para algumas das operações da classe de tipo :
que mostra que existem duas operações, igual
(==)
e diferente(/=)
, para coisas que são instâncias daEq
classe de tipo. Mas a operação não igual é definida em termos de iguais (de modo que você só precisa fornecer um) e vice-versa.Portanto, em Java provavelmente não legal, seria algo como:
e a maneira como funcionaria é que você só precisaria fornecer um desses métodos para implementar a interface. Portanto, eu diria que a capacidade de fornecer uma espécie de implementação parcial do comportamento que você deseja no nível da interface é uma diferença.
fonte
Eles são semelhantes (leia-se: têm uso semelhante) e provavelmente implementados de forma semelhante: funções polimórficas em Haskell assumem uma 'vtable' listando as funções associadas à typeclass.
Essa tabela geralmente pode ser deduzida em tempo de compilação. Isso provavelmente é menos verdadeiro em Java.
Mas esta é uma tabela de funções , não métodos . Os métodos são vinculados a um objeto, as typeclasses Haskell não.
Veja-os como os genéricos de Java.
fonte
Como diz Daniel, as implementações de interface são definidas separadamente das declarações de dados. E, como outros apontaram, há uma maneira direta de definir operações que usam o mesmo tipo livre em mais de um lugar. Portanto, é fácil definir
Num
como uma typeclass. Assim, em Haskell, obtemos os benefícios sintáticos da sobrecarga de operadores sem realmente ter nenhum operador sobrecarregado de mágica - apenas typeclasses padrão.Outra diferença é que você pode usar métodos baseados em um tipo, mesmo quando ainda não há um valor concreto desse tipo por aí!
Por exemplo
read :: Read a => String -> a
,. Portanto, se você tiver informações de outro tipo suficientes sobre como usará o resultado de uma "leitura", poderá deixar o compilador descobrir qual dicionário usar para você.Você também pode fazer coisas como o
instance (Read a) => Read [a] where...
que permite definir uma instância de leitura para qualquer lista de coisas legíveis. Não acho que isso seja possível em Java.E tudo isso são apenas typeclasses padrão de parâmetro único, sem truques acontecendo. Uma vez que introduzimos typeclasses multiparâmetros, um novo mundo de possibilidades se abre, e ainda mais com dependências funcionais e famílias de tipos, que permitem incorporar muito mais informações e computação no sistema de tipos.
fonte