Яка різниця між ознаками в Rust і typeclasses у Haskell?


157

Риси в Русті здаються принаймні поверхнево схожими на класи типу Хаскелл, проте я бачив, як люди пишуть, що між ними є деякі відмінності. Мені було цікаво, які саме ці відмінності.


8
Я мало знаю про Расту. Але загальні камені спотикання для подібних технологій в інших мовах є вищими типами (наприклад, чи можуть бути ознаки в межах параметризованих типів, але не за їх параметрами?) І поліморфізм типу "повернення" (наприклад, чи може такий тип ознаки з'явитися в результаті функції, але ніде в аргументах?). Приклад колишнього в Хаскеллі class Functor f where fmap :: (a -> b) -> (f a -> f b); прикладом останнього є class Bounded a where maxBound :: a.
Даніель Вагнер

4
GHC також підтримує багатопараметричні класи типів (тобто ознаки, що включають кілька типів) та функціональні залежності, хоча це не є частиною офіційної специфікації Haskell. Судячи із синтаксису Іржі, запропонованого у вашому посиланні, він може підтримувати лише риси, що мають розмір одного типу за один раз, хоча це судження знову не ґрунтується на глибокому досвіді.
Даніель Вагнер

4
@DanielWagner Поліморфізм типу зворотного типу існує (наприклад std::default), і багатопараметричні ознаки начебто працюють (включаючи аналог функціональних залежностей), хоча AFAIK потрібно обходити перший привілейований параметр. Однак немає HKT. Вони у списку бажань у майбутньому, але ще не на горизонті.

4
Ще одна відмінність - це лікування випадків сиріт. Іржа намагається мати більш жорсткі правила узгодженості того, де можна записати новий імпульс для ознаки. Дивіться цю дискусію для більш детальної інформації (зокрема тут )
Паоло Фалабелла,

1
Іржа підтримує пов'язані типи та обмеження рівності зараз, хоча вони не такі потужні, як сімейства типів Haskell. Він також має екзистенційні типи через об'єкти ознаки .
Фея лямбда

Відповіді:


61

На базовому рівні різниці немає, але вони все ще є.

Haskell описує функції або значення, визначені в класному класі, як "методи", подібно до того, як ознаки описують методи OOP в об'єктах, до яких вони додаються. Однак Хаскелл має справу з цим по-різному, трактуючи їх як індивідуальні цінності, а не прив’язуючи їх до об'єкта, як OOP призведе до цього. Йдеться про найбільш очевидну різницю на рівні поверхні.

Єдине, що Руст не міг зробити деякий час, - це риси , що набирають вищого порядку , такі як сумнозвісні Functorта Monadкласні типи.

Це означає, що риси Іржі могли описувати лише те, що часто називають "конкретним типом", іншими словами, без загального аргументу. Haskell з самого початку міг створювати типи класів вищого порядку, які використовують типи, аналогічні тому, як функції вищого порядку використовують інші функції: використовуючи одну для опису іншої. Протягом певного періоду це не було можливим у Іржі, але з тих пір, коли пов'язані елементи були реалізовані, такі риси стали звичними та ідіоматичними.

Тож якщо ми ігноруємо розширення, вони не зовсім однакові, але кожне може наблизити те, що може зробити інший.

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

Загалом, риси та класи класів мають принципові відмінності, які завдяки способу їх взаємодії змушують їх діяти і, зрештою, виглядають досить схожими.


† Приємну статтю про типові класи Хаскелла (включаючи вищі типи) можна знайти тут , а розділ Rust by Example про риси можна знайти тут


1
Іржа все ще не має жодної форми вищого роду. "Ганебним" потрібно виправдання. Функтор неймовірно поширений і корисний як концепція. Сімейства типів такі ж, як і асоційовані типи. Функціональні залежності по суті є надмірними щодо асоційованих типів (в тому числі в Haskell). У рісті не вистачає wrt. Фундаменти - це примітки про інноваційність. У вас це назад, риси Руста та типи Хаскелла різні на поверхні, але багато відмінностей випаровуються, коли ви дивитесь внизу.
Залишилися

Асоційовані предмети вважаються ідентичними за багатьох обставин, правда?
Ваелюс

@Vaelus Ти маєш рацію - цю відповідь слід трохи оновити. Редагування зараз.
AJFarmar

19

Я думаю, що нинішні відповіді не враховують найбільш фундаментальних відмінностей між ознаками Руста та класами типу Haskell. Ці відмінності пов'язані з тим, як риси пов'язані з об'єктно-орієнтованими мовними конструкціями. Докладніше про це див. У книзі Іржа .

  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ключовим словом. Для отримання додаткової інформації див., Наприклад, цю відповідь .


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 поширюються на типи вищого порядку. Риси іржі не підтримують лише типи вищого порядку, оскільки вони відсутні у всій мові, тобто це не філософська різниця між ознаками та класами типів


1
Риси Іржі не "втручаються лише в вирази з позначенням крапок". Наприклад, розглянемо Defaultознаку, яка не має методів, лише пов'язані з неметодом функції.
Сентриль
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.