:*:
is a tuple (almost):*:
is used to combine two types in the same way a tuple does.
The only difference is that :*:
carries around the additional p
parameter that all Generics types carry around.
data (,) a b = (,) a b
data (:*:) a b p = (:*:) (a p) (b p)
Haskell will use :*:
two represent constructors with multiple
parameters. These can be regular parameters or fields of a record.
Types with multiple parameters are sometimes called product types,
hence the multiplication symbol in :*:
. Here’s two examples of product
types.
data Ghost = Ghost { haunt :: Coords, opacity :: Double }
data Monster = Monster Species Odor HidingPlace
In the generics representation of a contructor with two parameters, like
Ghost
, those parameters land on opposite sides of a single :*:
. If
the constructor has three or more parameters, like Monster
, then it
takes multiple :*:
to combine them all. The generics representation
of Monster
looks like this:
-- pseudo code below, because wrappers like M1 and K1 are omitted!
type instance Rep Monster = Species :*: (Odor :*: HidingPlace)
A tuple value (2, "Hi!")
looks a lot like its type (Int, Text)
.
It’s the same with :*:
. A value of the zombie type, again omitting K1
and M1 wrappers, looks like this:
monster = Zombie :*: (Decay :*: UnderBed)