Alexander Kondratskiy
Alexander Kondratskiy

Reputation: 4275

Haskell: Can typeclasses define types (ala type traits)

Is it possible to have a type be part of a typeclass? Something like:

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

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

instance KeyTraits TableRow where
    keyType = Date
    key = date

And can these "type-level" functions be used elsewhere? For example:

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

I may be doing something completely wrong, but I basically want the ability to define type relationships like the one above (e.g. Certain values already may have data that can be used as a Key). If that's not possible, or is difficult, could you suggest a better design that is more idiomatic?

Thank you!

Upvotes: 11

Views: 674

Answers (2)

Vladimir Matveev
Vladimir Matveev

Reputation: 128111

Type families are exactly what you are looking for, but there is also another way to achieve their functionality, namely multi-parameter type classes with functional dependencies. With these extensions you code could look like this:

{-# 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

Here the associated type migrates to type class parameter, and the relationship between v and k, previously implicit, now becomes explicit with functional dependency.

This is completely equivalent to associated types, but IMO provides much more cleaner syntax, especially in functions which use your type class. Compare:

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

and

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

This becomes more apparent when more types and more type classes appear in single type declaration.

However, type families seem to be preferred by haskell community, and in fact the whole extension is more powerful than MPTC+FD, because type families can be declared without type classes, and there are also data families.

Upvotes: 11

user142019
user142019

Reputation:

Take a look at type families.

{-# 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 }

Upvotes: 26

Related Questions