Mathematics
Rust
Comments 15
+1
Спасибо за статью!

Не совсем корректно, имхо, называть трейты классами типов, потому что они всё-таки немного отличаются от функциональности классов типов из хаскеля. Хотя со временем различия размываются, да — скоро в трейты добавят (если уже не добавили, не помню) множественную диспетчеризацию и ассоциированные типы. В Rust очень сильно не хватает типов высшего порядка, поэтому многие вещи, нормальные для классов типов, через трейты невыразимы.

Трюк с None, к счастью, временный — с добавлением UFCS (хотя я не уверен, что его добавят до 1.0) нужда в нём отпадёт. Кстати, емнип, это
  let n: Option<T> = None;
  Float::mantissa_digits(n)

можно переписать как
  Float::mantissa_digits(None::<T>)
0
Множественная диспечеризация и ассоциированные типы и в Haskell не в базовом стандарте, а в расширениях.
Типов высших порядков сильно не хватает. Думаю, рано или поздно их вынуждены будут добавить.
0
>не следует путать с trait из Scala

А можете в двух словах пояснить принципиальное отличие? Я по приведенным примерам никаких отличий кроме синтаксиса не заметил.
+1
В Scala trait создает новый тип или добавляется при создании типа. В отличие от интерфейсов Java он может содержать реализацию некоторых методов. Есть еще тонкости взаимодействия нескольких трейтов, которые отличают их от множественного наследования (как в C++), но это совсем в сторону от Rust :-).
В любом случае этот интерфейс не будет присутствовать в объектах, созданных с помощью исходного типа.
В Rust описание trait не создает тип (в Haskell «вид» класса типов будет «функция из типа в ограничение» Num :: * -> Constraint, в Rust виды пока не реализованы, но смысл такой же). Реализация трейта тоже не создает новый тип — новым интерфейсом будут обладать объекты старого типа.
0
Но в чем разница с функциональной точки зрения, так сказать. Какая разница, будет ли интерфейс присутствовать в объектах, создаваемых с помощью исходного типа?
Т.е. ваш пример мог быть реализован в Scala как-нибудь так: Dual extends AnyRef with Add with Sub with…
ну и соответственно дальше реализация для Dual всех методов, предоставляемых трейтами Add, Sub и т.д.
Т.е. пока только разная форма одного и того же. А вопрос скорее про функционал. Т.е. что можно сделать с типом Dual в Rust, чего нельзя сделать с ним же в Scala, или наоборот. Или может при каком-то подходе кода меньше надо писать, или ещё что-то, важное с точки зрения потребителя этих технологий.
0
Например, я поленился реализовать FloatMath, а реализовал только Float. И решил не давать исходников. Но пользователь моей библиотеки сможет реализовать FloatMath, при этом сохранив весь код, который работает с моим Dual. В Scala это делается с помощью implicits.
Еще есть отличие в использовании памяти — в ООП ссылка на таблицу методов хранится в каждом объекте, с использованием классов типов эта ссылка таскается по стеку. Но это не сейчас уже принципиально :-).
0
Очень интересно (побольше бы таких статей на Хабре!), правда слишком кратко и сжато, хотелось бы более подробно и последовательно.
А что такое «классы типов» в Go? То что там называется ключевым словом «interface» или что-то другое?
Что такое «типы высших порядков»?
+1
Тип высших порядков, это когда параметризованный тип получает параметром другой параметризованный тип (без фиксации значения параметра). Например, тип может быть параметризован контейнером, а внутри себя создавать этот контейнер и с целыми, и со строками. Haskell поддерживает классы «шаблонов типов», параметризованных типов без параметра. Например, Monad содержет IO, Maybe, списки [] — все это еще не типы, им нужен параметр IO String, [Int] и тп.

В Go я глубоко не заглядывал. К сожалению, не могу сейчас сказать, как там описываются классы типов. Помню только, что они есть.
0
А что такое «классы типов» в Go? То что там называется ключевым словом «interface» или что-то другое?
Что такое «типы высших порядков»?


Кмк, в Go нет полноценных классов типов. Например, неясно, как там сделать тот же Zero или Add, сохранив строгую типизацию. Т.е. в теории можно везде использовать object и потом приводить тип, но это уже совсем не то.
0
Но тип Self на момент компиляции еще не известен — он задается параметром шаблона. А значит метод zero динамически находится в таблице методов реализации Num для Dual!
Методы трейтов могут искаться либо на этапе компиляции (аналогично тому как работают шаблоны в C++) либо во время выполнения (аналогично тому как работают абстрактные классы и виртуальные функции в С++).
0
Что такое Self, не сказано (хотя догадаться можно, конечно). Сказать тем более полезно, что это отличие от классов типов в других языках.
0
А кстати, можно ли написать дефолтовые реализации для всех методов через друг друга, чтобы достаточно было определить хотя бы одну, как можно в Haskell? Например, для eq:
pub trait PartialEq {
    /// This method tests for `self` and `other` values to be equal, and is used by `==`.
    fn eq(&self, other: &Self) -> bool { !self.ne(other) }

    /// This method tests for `!=`.
    #[inline]
    fn ne(&self, other: &Self) -> bool { !self.eq(other) }
}
Only those users with full accounts are able to leave comments., please.