Reputation: 4275
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
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
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