примеры использования семейства данных

Преимущества использования семейств типов synonym очевидны — это функции уровня типа.

Но это не относится к семействам data, поэтому мой вопрос: каковы варианты использования для семейств данных? Где мне его использовать?


person John Rivers    schedule 07.01.2013    source источник
comment
Вместо того, чтобы просто указать на существующий тип, иногда хочется еще и свой свежий тип, оборачивающий существующий или просто обычную запись с различными полями, а не просто голый синоним существующему. Вы можете использовать их точно так же, как вы обычно используете типы, объявленные с «данными», за исключением того, что на этот раз представление будет другим в зависимости от аргумента (ов) типа, который вы ему дадите.   -  person Alp Mestanogullari    schedule 07.01.2013


Ответы (1)


Одним из преимуществ является то, что семейства данных являются инъективными, в отличие от семейств типов.

Если у вас есть

type family TF a
data family DF a

Тогда вы знаете, что DF a ~ DF b подразумевает, что a ~ b, в то время как с TF вы этого не делаете -- для любого a вы можете быть уверены, что DF a является совершенно новым типом (точно так же, как [a] - это тип, отличный от [b], если, конечно, a ~ b), в то время как семейство типов может сопоставлять несколько типов ввода с одним и тем же существующим типом.

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

Это не особенно реальный пример, но, например, вы можете сделать:

data instance DF Int    = DInt    Int
data instance DF String = DString String

class C t where
    foo :: t Int -> t String

instance C DF where -- notice we are using DF without an argument
                    -- notice also that you can write instances for data families at all,
                    -- unlike type families
    foo (DInt i) = DString (show i)

По сути, DF и DF a сами по себе являются реальными, первоклассными, законными типами, как и любой другой тип, который вы объявляете с помощью data. TF a — это просто промежуточная форма, которая оценивается как тип.

Но я полагаю, что все это не очень поучительно, или, по крайней мере, не было для меня, когда я интересовался семействами данных и читал подобные вещи.

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

Реальный пример из библиотеки vector. vector имеет несколько различных типов векторов: упакованные векторы, неупакованные векторы, примитивные векторы, сохраняемые векторы. Для каждого типа Vector существует соответствующий изменяемый тип MVector (обычные векторы неизменяемы). Так это выглядит так:

type family Mutable v :: * -> * -> * -- the result type has two type parameters

module Data.Vector{.Mutable} where
data Vector a = ...
data MVector s a = ...
type instance Mutable Vector = MVector

module Data.Vector.Storable{.Mutable} where
data Vector a = ...
data MVector s a = ...
type instance Mutable Vector = MVector

[etc.]

Теперь вместо этого я бы предпочел:

data family Mutable v :: * -> * -> *

module Data.Vector{.Mutable} where
data Vector a = ...
data instance Mutable Vector s a = ...
type MVector = Mutable Vector

module Data.Vector.Storable{.Mutable} where
data Vector a = ...
data instance Mutable Vector s a = ...
type MVector = Mutable Vector

[etc.]

Который кодирует инвариант, согласно которому для каждого типа Vector существует ровно один тип Mutable Vector и что между ними существует однозначное соответствие. Изменяемая версия Vector всегда называется Mutable Vector: это ее имя, и другого у нее нет. Если у вас есть Mutable Vector, вы можете получить тип соответствующего неизменяемого Vector, потому что он прямо здесь как аргумент типа. С type family Mutable, как только вы применяете его к аргументу, он возвращает неуказанный тип результата (предположительно называемый MVector, но вы не можете этого знать), и у вас нет возможности отобразить в обратном направлении.

person glaebhoerl    schedule 07.01.2013