Haskell: Алгебраические данные против Tuple
data Ray = Ray Vector Vector
или
type Ray = (Vector, Vector)
Что предпочтительнее в идиоматическом haskell? Почему я должен использовать один над другим?
Меня не волнует производительность.
Кажется, что это мало чем отличается от функций, например:
trace :: Ray -> …
trace (Ray x d) = …
-- OR
trace (x, d) = …
Ответы
Ответ 1
Версия data
предпочтительнее, поскольку она более четко указывает на намерение программиста - создавая новый тип, вы указываете на все, что это не просто кортеж, а значимая семантическая сущность, a Ray
.
Это позволяет далее опираться на систему типов с пользовательскими экземплярами для Ray
, а оптимизация невозможна в кортежах.
Ответ 2
Вы также можете рассмотреть третий вариант, который представляет собой комбинацию из двух: newtype
newtype Ray = Ray (Vector, Vector)
Алгебраические типы данных, на мой взгляд, используются в ситуациях, когда у вас есть несколько альтернатив, или в тех случаях, когда вам нужен тип, который будет рекурсивным, содержащий себя. Но это может быть излишним для чего-то подобного.
Дон Стюарт отметил, что создание синонима типа для кортежа - это то же самое, что и использование этого типа кортежа; синонимы типов не имеют собственной идентичности. Таким образом, средство проверки типов не сможет различать ваш тип и кортеж, и поэтому он не может проверить, что вы используете тот тип, который вам нужен. Кроме того, он имел бы те же экземпляры, что и кортеж.
A newtype
позволяет использовать тот же базовый тип, что и кортеж; но это отдельный тип для проверки типов, с отдельными экземплярами.
Ответ 3
Четвертая альтернатива, которую я нашел довольно удобной, - это записи:
data Ray = Ray { from, to :: Vector }
У них есть в основном все функции "нормальных" ADT, но с некоторым дополнительным синтаксическим сахаром. Особенно они облегчают получение частично модифицированных копий значения. Правда, в некоторых ситуациях записи слишком ограничены, но тогда вы можете продолжить "улучшенные версии", например fclabels.