Traços em Rust parecem pelo menos superficialmente semelhantes às classes de letras em Haskell, no entanto, vi pessoas escreverem que existem algumas diferenças entre eles. Fiquei me perguntando exatamente quais são essas diferenças.
157
class Functor f where fmap :: (a -> b) -> (f a -> f b)
; um exemplo do último éclass Bounded a where maxBound :: a
.std::default
), e traços multiparâmetros funcionam como um tipo de trabalho (incluindo um análogo de dependências funcionais), embora o AFAIK precise solucionar o primeiro parâmetro privilegiado. No entanto, HKT. Eles estão na lista de desejos de futuro distante, mas ainda não estão no horizonte.Respostas:
No nível básico, não há muita diferença, mas eles ainda estão lá.
Haskell descreve funções ou valores definidos em uma classe de tipo como 'métodos', assim como os traços descrevem os métodos OOP nos objetos que eles incluem. No entanto, Haskell lida com isso de maneira diferente, tratando-os como valores individuais, em vez de fixá-los a um objeto, como o OOP levaria a pessoa a fazer. Essa é a diferença mais óbvia no nível da superfície que existe.
A única coisa que Rust não pôde fazer por um tempo foram os caracteres tipificados de ordem superior , como as infames
Functor
e asMonad
classes tipográficas.Isso significa que os traços de Rust só poderiam descrever o que é chamado de "tipo concreto", em outras palavras, um sem argumento genérico. Desde o início, Haskell pode criar classes de ordem superior, que usam tipos semelhantes a como as funções de ordem superior usam outras funções: usar uma para descrever outra. Por um período de tempo, isso não foi possível no Rust, mas desde que os itens associados foram implementados, esses traços se tornaram comuns e idiomáticos.
Portanto, se ignorarmos as extensões, elas não serão exatamente iguais, mas cada uma poderá se aproximar do que a outra pode fazer.
Também é mencionável, como dito nos comentários, que o GHC (compilador principal de Haskell) suporta mais opções para classes de tipos, incluindo classes de tipos multiparâmetros (isto é, muitos tipos envolvidos) e dependências funcionais , uma opção adorável que permite cálculos em nível de tipo , e leva a digitar famílias . Que eu saiba, Rust não tem departamentos divertidos nem famílias de tipos, embora possa no futuro. †
Em suma, traços e classes de tipos têm diferenças fundamentais, que devido à maneira como interagem, os fazem agir e parecem bastante semelhantes no final.
† Um bom artigo sobre as classes de tipo de Haskell (incluindo as de tipo mais alto) pode ser encontrado aqui , e o capítulo Rust by Example sobre características pode ser encontrado aqui
fonte
Penso que as respostas atuais ignoram as diferenças mais fundamentais entre os traços de Rust e as classes do tipo Haskell. Essas diferenças têm a ver com a maneira como os traços estão relacionados às construções de linguagem orientada a objetos. Para obter informações sobre isso, consulte o livro Rust .
Uma declaração de característica cria um tipo de característica . Isso significa que você pode declarar variáveis desse tipo (ou melhor, referências do tipo). Você também pode usar tipos de características como parâmetros em funções, campos struct e instanciações de parâmetros de tipo.
Uma variável de referência de característica pode, em tempo de execução, conter objetos de tipos diferentes, desde que o tipo de tempo de execução do objeto referenciado implemente a característica.
Não é assim que as classes de tipos funcionam. As classes de tipos não criam tipos ; portanto, você não pode declarar variáveis com o nome da classe. As classes de tipo agem como limites nos parâmetros de tipo, mas os parâmetros de tipo devem ser instanciados com um tipo concreto, não com a própria classe de tipo.
Você não pode ter uma lista de coisas diferentes de tipos diferentes que implementam a mesma classe de tipo. (Em vez disso, os tipos existenciais são usados no Haskell para expressar uma coisa semelhante.) Nota 1
Os métodos de características podem ser despachados dinamicamente . Isso está fortemente relacionado às coisas descritas na seção acima.
A expedição dinâmica significa que o tipo de tempo de execução do objeto que uma referência aponta é usado para determinar qual método é chamado pela referência.
Novamente, tipos existenciais são usados para isso em Haskell.
Em conclusão
Parece-me que características são, em muitos aspectos, o mesmo conceito que classes de tipo. Além disso, eles têm a funcionalidade de interfaces orientadas a objetos.
Por outro lado, as classes de tipo de Haskell são mais avançadas. Haskell tem, por exemplo, tipos e extensões de tipo mais alto, como classes de tipo com vários parâmetros.
Nota 1 : As versões recentes do Rust possuem uma atualização para diferenciar o uso de nomes de características como tipos e o uso de nomes de características como limites. Em um tipo de característica, o nome é prefixado pela
dyn
palavra - chave. Veja, por exemplo, esta resposta para mais informações.fonte
dyn Trait
como uma forma de digitação existencial, pois elas se relacionam a traços / classes de tipos. Podemos considerardyn
um operador dentro dos limites projetando-os para tipos, iedyn : List Bound -> Type
. Tomando essa idéia para Haskell, e com respeito aos "então você não pode declarar variáveis com o nome da classe", podemos fazer isso indiretamente em Haskell:data Dyn (c :: * -> Constraint) = forall (t :: Type). c t => D t
. Tendo definido isso, podemos trabalhar[D True, D "abc", D 42] :: [D Show]
.As "características" de Rust são análogas às classes de tipo de Haskell.
A principal diferença com Haskell é que os traços intervêm apenas para expressões com notação de ponto, ou seja, da forma a.foo (b).
As classes do tipo Haskell se estendem aos tipos de ordem superior. As características de ferrugem apenas não suportam tipos de ordem superior porque estão ausentes em todo o idioma, ou seja, não é uma diferença filosófica entre características e classes de tipo
fonte
Default
característica que não possui métodos, apenas funções associadas a outros métodos.