В чем разница между чертами в Rust и классами типов в Haskell?

157

Черты в Rust кажутся по крайней мере внешне похожими на классы типов в Haskell, однако я видел, как люди пишут, что между ними есть некоторые различия. Мне было интересно, что именно эти различия.

LogicChains
источник
8
Я не знаю много о Rust. Но общими камнями преткновения для подобных технологий в других языках являются более высокие виды (например, могут ли характеристики варьироваться в зависимости от параметризованных типов, но не их параметров?) И полиморфизм возвращаемого типа (например, может ли тип признаков появляться в результате функции, но не где-либо еще). в аргументах?). Примером первого в Хаскеле является class Functor f where fmap :: (a -> b) -> (f a -> f b); Примером последнего является class Bounded a where maxBound :: a.
Даниэль Вагнер
4
GHC также поддерживает многопараметрические классы типов (то есть признаки, включающие несколько типов) и функциональные зависимости, хотя это не является частью официальной спецификации Haskell. Судя по синтаксису Rust, предложенному по вашей ссылке, он может поддерживать только черты, варьирующиеся по одному типу за раз, хотя это суждение опять-таки не основано на глубоком опыте.
Даниэль Вагнер
4
@DanielWagner Полиморфизм возвращаемого типа существует (например, std::default), и многопараметрические характеристики являются своего рода работой (включая аналог функциональных зависимостей), хотя AFAIK необходимо обойти первый привилегированный параметр. Нет HKT однако. Они находятся в списке желаний в далеком будущем, но еще не на горизонте.
4
Другое различие заключается в лечении сиротских случаев. Rust пытается установить более строгие правила согласования того, где можно написать новый признак для черты. Смотрите это обсуждение для более подробной информации (в частности, здесь )
Паоло Фалабелла
1
Rust теперь поддерживает связанные типы и ограничения равенства , хотя они не так мощны, как семейства типов Haskell. Он также имеет экзистенциальные типы через объекты признаков .
Лямбда-фея

Ответы:

61

На базовом уровне особой разницы нет, но они все еще есть.

Haskell описывает функции или значения, определенные в классе типов, как «методы», так же, как черты описывают методы ООП в объектах, которые они включают. Тем не менее, Haskell работает с ними по-разному, рассматривая их как отдельные значения, а не прикрепляя их к объекту, как это может привести ООП. Это примерно самая очевидная разница на уровне поверхности.

Единственное, что Руст не мог сделать какое-то время , это типизированные черты высшего порядка , такие как печально известные Functorи Monadклассы типов.

Это означает, что признаки Rust могут описывать только то, что часто называют «конкретным типом», другими словами, тип без общего аргумента. Haskell с самого начала мог создавать классы типов высшего порядка, которые используют типы, аналогичные тому, как функции высшего порядка используют другие функции: используя одну для описания другой. В течение некоторого времени это было невозможно в Rust, но, поскольку связанные элементы были реализованы, такие черты стали обычным и идиоматическим.

Поэтому, если мы игнорируем расширения, они не совсем одинаковы, но каждое из них может приблизительно соответствовать тому, что может сделать другое.

Также следует упомянуть, как сказано в комментариях, что GHC (основной компилятор Haskell) поддерживает дополнительные параметры для классов типов, включая многопараметрические (т.е. включающие много типов) классы типов и функциональные зависимости , прекрасный вариант, который допускает вычисления на уровне типов и приводит к типу семей . Насколько мне известно, у Rust нет ни funDeps, ни семейства типов, хотя это может произойти в будущем. †

В целом, черты и классы типов имеют фундаментальные различия, которые из-за того, как они взаимодействуют, заставляют их действовать и в конечном итоге кажутся довольно похожими.


† Хорошая статья о классах типов Хаскелла (включая более типизированные) может быть найдена здесь , и глава Rust by Example о чертах может быть найдена здесь

AJFarmar
источник
1
Ржавчина до сих пор не имеет какой-либо формы более высоких видов. «Печально» нуждается в обосновании. Функторы невероятно распространены и полезны как концепция. Семейства типов такие же, как связанные типы. Функциональные зависимости по существу избыточны со связанными типами (в том числе в Haskell). То, чего не хватает в Rust. fundeps - это аннотации инъекций. У вас это задом наперед, черты характера Rust и классы типов Haskell различаются на поверхности, но многие различия исчезают, когда вы смотрите под ними. Различия, которые остаются, в основном присущи различным областям, в которых работают языки.
Centril
Сопутствующие предметы в настоящее время считаются странными во многих обстоятельствах, верно?
Vaelus
@Vaelus Вы правы - этот ответ должен быть немного обновлен. Редактирование сейчас.
AJFarmar
19

Я думаю, что текущие ответы упускают из виду самые фундаментальные различия между чертами Rust и классами типа Haskell. Эти различия связаны с тем, как черты связаны с объектно-ориентированными языковыми конструкциями. Информацию об этом смотрите в книге Rust .

  1. Объявление черты создает тип черты . Это означает, что вы можете объявить переменные такого типа (или, скорее, ссылки типа). Вы также можете использовать типы признаков в качестве параметров функций, структурных полей и экземпляров параметров типа.

    Ссылочная переменная признака может во время выполнения содержать объекты различных типов, если тип выполнения ссылочного объекта реализует признак.

    // 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();

    Это не то, как работают классы типов. Классы типов не создают типов , поэтому вы не можете объявлять переменные с именем класса. Классы типов действуют как границы параметров типов, но параметры типа должны создаваться конкретным типом, а не самим классом типа.

    Вы не можете иметь список разных вещей разных типов, которые реализуют один и тот же класс типов. (Вместо этого экзистенциальные типы используются в Haskell для выражения аналогичной вещи.) Примечание 1

  2. Методы черты могут быть динамически отправлены . Это сильно связано с тем, что описано в разделе выше.

    Динамическая диспетчеризация означает, что тип времени выполнения объекта, на который ссылаются точки, используется для определения того, какой метод вызывается через ссылку.

    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());

    Снова, экзистенциальные типы используются для этого в Haskell.

В заключение

Мне кажется, что черты во многих отношениях совпадают с концепцией классов. Кроме того, они обладают функциональностью объектно-ориентированных интерфейсов.

С другой стороны, классы типов в Haskell более продвинуты. Haskell имеет, например, типы с более высоким родом и расширения, такие как классы многопараметрических типов.


Примечание 1. В последних версиях Rust есть обновление, позволяющее дифференцировать использование имен признаков в качестве типов и использование имен признаков в качестве границ. В типе признаков имя начинается с dynключевого слова. Смотрите, например, этот ответ для получения дополнительной информации.

Lii
источник
2
«Классы типов не создают типов» - я думаю, что лучше всего понимать их dyn Traitкак форму экзистенциальной типизации, поскольку они относятся к классам признаков / типов. Мы можем рассмотреть dynоператор в пределах выступающий их тип, то есть dyn : List Bound -> Type. Принимая эту идею Haskell, и в отношении «так что вы не можете объявлять переменные с именем класса», мы можем сделать это косвенно в Haskell: data Dyn (c :: * -> Constraint) = forall (t :: Type). c t => D t. Определив это, мы можем работать с [D True, D "abc", D 42] :: [D Show].
Центриль
8

«Черты» Руста аналогичны классам типов Хаскелла.

Основное отличие от Haskell заключается в том, что черты вмешиваются только для выражений с точечной нотацией, то есть в форме a.foo (b).

Классы типов Haskell распространяются на типы высшего порядка. Черты Rust не поддерживают только типы высшего порядка, потому что они отсутствуют во всем языке, т.е. это не философское различие между чертами и классами типов.

Анудж Гупта
источник
1
Черты в Rust не «вмешиваются только в выражения с точечной нотацией». Например, рассмотрим Defaultпризнак, у которого нет методов, только функции, не связанные с методами.
Центриль