Jaka jest różnica między cechami w Rust a typeklasami w Haskell?


157

Cechy w Rust wydają się przynajmniej powierzchownie podobne do typeklas w Haskell, jednak widziałem, jak ludzie piszą, że są między nimi pewne różnice. Zastanawiałem się dokładnie, jakie są te różnice.


8
Niewiele wiem o Rust. Jednak częstymi przeszkodami dla podobnych technologii w innych językach są typy wyższe (np. Czy cechy mogą wykraczać poza typy sparametryzowane, ale nie ich parametry?) I polimorfizm typu zwracanego (np. Czy typ cechy może pojawić się w wyniku funkcji, ale nie nigdzie w argumentach?). Przykładem tego pierwszego w Haskell jest class Functor f where fmap :: (a -> b) -> (f a -> f b); przykładem tego ostatniego jest class Bounded a where maxBound :: a.
Daniel Wagner

4
GHC obsługuje również klasy typu wieloparametrowego (czyli cechy obejmujące kilka typów) i zależności funkcjonalne, chociaż nie jest to część oficjalnej specyfikacji Haskella. Sądząc po składni Rusta sugerowanej w twoim linku, może on obsługiwać tylko cechy z zakresu jednego typu naraz, chociaż ten osąd ponownie nie jest oparty na głębokim doświadczeniu.
Daniel Wagner

4
@DanielWagner Polimorfizm typu zwrotnego istnieje (np. std::default) I wieloparametrowe cechy sort-of work (w tym analog zależności funkcjonalnych), chociaż AFAIK trzeba obejść uprzywilejowany pierwszy parametr. Jednak żaden HKT. Są na liście życzeń dalekiej przyszłości, ale jeszcze nie na horyzoncie.

4
inną różnicą jest traktowanie przypadków sierocych. Rust stara się mieć ściślejsze reguły spójności określające, gdzie można napisać nowy implik dla cechy. Zobacz tę dyskusję po więcej szczegółów (w szczególności tutaj )
Paolo Falabella

1
Rust obsługuje teraz powiązane typy i ograniczenia równości , chociaż nie są one tak potężne, jak rodziny typów Haskella. Ma również typy egzystencjalne poprzez obiekty cech .
Lambda Fairy

Odpowiedzi:


61

Na podstawowym poziomie nie ma dużej różnicy, ale nadal istnieją.

Haskell opisuje funkcje lub wartości zdefiniowane w typeklasie jako „metody”, tak jak cechy opisują metody OOP w obiektach, które obejmują. Jednak Haskell traktuje je w inny sposób, traktując je jako indywidualne wartości, zamiast przypinać je do obiektu, jak prowadziłby OOP. Chodzi o najbardziej oczywistą różnicę w poziomach powierzchni.

Jedyną rzeczą, której Rust nie mógł zrobić przez jakiś czas, były cechy typizowane wyższego rzędu , takie jak niesławne FunctoriMonadJedyną typizowane typeklasy.

Oznacza to, że cechy Rusta mogą opisywać tylko to, co często nazywa się „typem konkretnym”, innymi słowy, bez argumentu ogólnego. Haskell od samego początku mógł tworzyć typeklasy wyższego rzędu, które używają typów podobnych do tego, jak funkcje wyższego rzędu używają innych funkcji: używając jednej do opisania drugiej. Przez pewien czas nie było to możliwe w Rust, ale od tego czasu zaimplementowano powiązane przedmioty , takie cechy stały się powszechne i idiomatyczne.

Więc jeśli zignorujemy rozszerzenia, nie są one dokładnie takie same, ale każde z nich może przybliżać, co może zrobić drugie.

Należy również wspomnieć, jak powiedziano w komentarzach, że GHC (główny kompilator Haskella) obsługuje dalsze opcje dla typeklas, w tym typeklasy wieloparametrowe (tj. Wiele typów) i zależności funkcjonalne , urocza opcja, która pozwala na obliczenia na poziomie typu i prowadzi do rodzin typów . O ile mi wiadomo, Rust nie ma ani funDeps, ani rodzin typów, chociaż może się to zdarzyć w przyszłości. †

Podsumowując, cechy i typeklasy mają fundamentalne różnice, które ze względu na sposób, w jaki wchodzą w interakcje, sprawiają, że zachowują się i ostatecznie wydają się dość podobne.


† Przyjemny artykuł o typeklasach Haskella (w tym o wyższych typach) można znaleźć tutaj , a rozdział Rust by Example dotyczący cech można znaleźć tutaj


1
Rdza nadal nie ma żadnej formy wyższego rodzaju. „Niesławny” wymaga uzasadnienia. Functory są niezwykle wszechobecne i przydatne jako koncepcja. Rodziny typów są takie same, jak typy skojarzone. Zależności funkcjonalne są zasadniczo zbędne w stosunku do skojarzonych typów (w tym w Haskell). To, czego Rust brakuje. fundeps to adnotacje dotyczące iniekcji. Masz to wstecz, cechy Rusta i klasy typu Haskella są różne na powierzchni, ale wiele różnic wyparowuje, gdy spojrzysz pod spód. Różnice, które pozostają w większości są nieodłącznym różnych dziedzinach języki działają w.
Centril

Powiązane przedmioty są teraz uważane za idomatyczne w wielu okolicznościach, prawda?
Vaelus

@Vaelus Masz rację - ta odpowiedź powinna zostać nieco zaktualizowana. Edycja teraz.
AJFarmar

19

Myślę, że obecne odpowiedzi pomijają najbardziej fundamentalne różnice między cechami Rusta a klasami typu Haskell. Różnice te mają związek ze sposobem, w jaki cechy są powiązane z konstrukcjami języka zorientowanego obiektowo. Więcej informacji na ten temat można znaleźć w książce Rust .

  1. Deklaracja cechy tworzy typ cechy . Oznacza to, że można zadeklarować zmienne tego typu (a raczej referencje tego typu). Możesz również użyć typów cech jako parametrów w polach funkcji, strukturach i wystąpieniach parametrów typu.

    Zmienna odniesienia cechy może w czasie wykonywania zawierać obiekty różnych typów, o ile typ środowiska wykonawczego obiektu, do którego się odwołuje, implementuje tę cechę.

    // The shape variable might contain a Square or a Circle, 
    // we don't know until runtime
    let shape: &Shape = get_unknown_shape();
    
    // Might contain different kinds of shapes at the same time
    let shapes: Vec<&Shape> = get_shapes();

    Nie tak działają klasy typów. Klasy typów nie tworzą żadnych typów , więc nie można deklarować zmiennych o nazwie klasy. Klasy typu działają jako granice parametrów typu, ale parametry typu muszą być utworzone z konkretnym typem, a nie z samą klasą typu.

    Nie możesz mieć listy różnych rzeczy różnych typów, które implementują tę samą klasę typów. (Zamiast tego, typy egzystencjalne są używane w Haskell do wyrażenia podobnej rzeczy.) Uwaga 1

  2. Metody cech można przesyłać dynamicznie . Jest to ściśle związane z rzeczami opisanymi w powyższej sekcji.

    Dynamiczne wysyłanie oznacza, że ​​typ środowiska wykonawczego obiektu, do którego punkty odniesienia, jest używany do określenia, która metoda jest wywoływana za pośrednictwem odwołania.

    let shape: &Shape = get_unknown_shape();
    
    // This calls a method, which might be Square.area or
    // Circle.area depending on the runtime type of shape
    print!("Area: {}", shape.area());

    Ponownie, w Haskellu używane są do tego typy egzystencjalne.

Na zakończenie

Wydaje mi się, że cechy są pod wieloma względami tym samym pojęciem, co klasy typu. Ponadto mają funkcjonalność interfejsów zorientowanych obiektowo.

Z drugiej strony klasy typów Haskella są bardziej zaawansowane. Haskell ma na przykład typy wyższego rzędu i rozszerzenia, takie jak klasy typu wieloparametrowego.


Uwaga 1 : Najnowsze wersje Rusta mają aktualizację mającą na celu zróżnicowanie używania nazw cech jako typów oraz używania nazw cech jako granic. W typie cechy nazwa jest poprzedzona dynsłowem kluczowym. Zobacz na przykład tę odpowiedź, aby uzyskać więcej informacji.


2
„Klasy typów nie tworzą typów” - myślę, że najlepiej jest rozumieć je dyn Traitjako formę egzystencjalnego typowania, ponieważ odnoszą się one do cech / klas typów. Możemy rozważyć dynoperator na granicach rzutujący je na typy, tj dyn : List Bound -> Type. Biorąc ten pomysł do Haskell, a w odniesieniu do „, więc nie można deklarować zmiennych z nazwą klasy”, możemy to zrobić pośrednio w Haskell: data Dyn (c :: * -> Constraint) = forall (t :: Type). c t => D t. Po zdefiniowaniu tego możemy pracować z [D True, D "abc", D 42] :: [D Show].
Centril

8

„Cechy” Rusta są analogiczne do klas typów Haskella.

Główna różnica w porównaniu z Haskellem polega na tym, że cechy występują tylko w przypadku wyrażeń z notacją kropkową, tj. Postaci a.foo (b).

Klasy typów Haskell obejmują typy wyższego rzędu. Cechy Rusta nie obsługują tylko typów wyższego rzędu, ponieważ brakuje ich w całym języku, tj. Nie jest to filozoficzna różnica między cechami a klasami typów


1
Cechy w Rust nie „wpływają tylko na wyrażenia z notacją kropkową”. Na przykład rozważmy Defaultcechę, która nie ma metod, a jedynie funkcje niezwiązane z metodami.
Centril
Korzystając z naszej strony potwierdzasz, że przeczytałeś(-aś) i rozumiesz nasze zasady używania plików cookie i zasady ochrony prywatności.
Licensed under cc by-sa 3.0 with attribution required.