Haskell: могут ли типы типов определять типы (черты типа ala)

Возможно ли, чтобы тип был частью класса? Что-то вроде:

class KeyTraits v where
    keyType :: *
    key :: v -> keyType

data TableRow = { date :: Date, metaData :: String, value :: Int }

instance KeyTraits TableRow where
    keyType = Date
    key = date

И могут ли эти функции типа "типа" использоваться в другом месте? Например:

-- automatically deduce the type for the key, from the value type, using
-- the typeclass
data MyMap v = { getMap :: (KeyTraits v) => Map (keyType) v }

Я могу делать что-то совершенно неправильное, но я в основном хочу, чтобы определить отношения типа, такие как выше (например, некоторые значения уже могут иметь данные, которые можно использовать в качестве ключа). Если это невозможно или сложно, можете ли вы предложить лучший дизайн, более идиоматический?

Спасибо!

Ответ 1

Взгляните на типы семейств.

{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE RankNTypes #-}

class KeyTraits k where
    type KeyType k :: *
    key :: v -> KeyType k

data TableRow = TableRow { date :: Date, metaData :: String, value :: Int }

instance KeyTraits TableRow where
    type KeyType TableRow = Date
    key = date

data MyMap v = MyMap { getMap :: (KeyTraits v) => Map (KeyType v) v }

Ответ 2

Типы типов - это именно то, что вы ищете, но есть еще один способ добиться их функциональности, а именно классы с несколькими параметрами с функциональными зависимостями. С этими расширениями код может выглядеть так:

{-# LANGUAGE MultiParamTypeClasses, FunctionalDependencies #-}

class KeyTraits v k | v -> k where
    key :: v -> k

data TableRow = { date :: Date, metaData :: String, value :: Int }

instance KeyTraits TableRow Date where
    key = date

Здесь связанный тип переносится на тип параметра класса, а связь между v и k, ранее неявная, теперь становится явной с функциональной зависимостью.

Это полностью эквивалентно связанным типам, но IMO обеспечивает гораздо более чистый синтаксис, особенно в функциях, которые используют ваш тип. Для сравнения:

getMap :: (KeyTraits v) => Map (KeyType v) v

и

getMap :: (KeyTraits k v) => Map k v

Это становится более очевидным, когда в объявлении одного типа появляются больше типов и классов типов.

Однако, семейство типов кажется предпочтительным сообществом haskell, и на самом деле все расширение более мощное, чем MPTC + FD, поскольку семейства типов могут быть объявлены без классов типов, а также есть семейства данных.