I learned that a type
synonym is a new name for an existing type. It can be used like
type MyChar = Char
But what does this usage mean like this?
class HasField a where
type FieldType a :: *
That's an associated type family, an extension provided by GHC if you use the pragma
{-# LANGUAGE TypeFamilies #-}
or pass the parameter -XTypeFamilies
to GHC or GHCi.
Basically, it declares a class such that each instance of the class can define individually what the type synonym means. For example:
data MyDataType = MyDataConstructor Int
instance HasField MyDataType where
type FieldType MyDataType = Int
All of this is a bit advanced so if you're just getting started with Haskell don't feel like you have to understand this right away.
That said, I'll add a simple example to Ørjan's answer, imagine we defined a class like this:
-- | Class for types that can be constructed from a list of items.
class Unfoldable t where
fromList :: [a] -> t a
Now we can define instances for various types:
import Data.Set (Set)
import qualified Data.Set as Set
instance Unfoldable [] where
fromList = id
instance Unfoldable Set where
fromList = Set.fromList
But this has two weaknesses:
ByteString
and Text
are monomorphic—their element type is hardcoded to Char8
and Char
respectively.fromList :: Ord k = [(k, v)] -> Map k v, but that definition doesn't support it, because
(k, v)is not a type parameter of
Map`.So using TypeFamilies
it's possible to improve it:
{-# LANGUAGE TypeFamilies, ConstraintKinds #-}
import Data.Monoid
import Data.Set (Set)
import qualified Data.Set as Set
import Data.Map (Map)
import qualified Data.Map as Map
import Data.Text (Text, pack)
import GHC.Exts (Constraint)
class Monoid m => Unfoldable m where
type Element m :: *
type Constraint m :: GHC.Exts.Constraint
type Constraint m = ()
fromList :: [Element m] -> m
instance Unfoldable [a] where
type Element [a] = a
fromList as = as
instance Ord a => Unfoldable (Set a) where
type Element (Set a) = a
type Constraint (Set a) = Ord a
fromList = Set.fromList
instance Ord k => Unfoldable (Map k v) where
type Element (Map k v) = (k, v)
type Constraint (Map k v) = Ord k
fromList = Map.fromList
instance Unfoldable Text where
type Element Text = Char
fromList = pack
Look at the type of fromList :: Monoid m => [Element m] -> m
. Basically, Element m
is a synonym whose expansion is different for each differenct choice of m
:
Element [a] := a
Element (Map k v) := (k ,v)
Element Text := Char
The other trick here is the use of ConstraintKinds
to allow each class instance to require individualized constraints on the type variables (e.g., Ord k
for Map
). That's a topic for another day...
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With