Could someone give a super simple (few line) example to get a basic understanding about what type families can be used for and what are they ?
The 2+2 kind of exampl
Here's an example:
{-# Language TypeFamilies, DataKinds, KindSignatures, GADTs, UndecidableInstances #-}
data Nat = Z | S Nat
type family Plus (x :: Nat) (y :: Nat) :: Nat where
Plus 'Z y = y
Plus ('S x) y = 'S (Plus x y)
data Vec :: Nat -> * -> * where
Nil :: Vec 'Z a
Cons :: a -> Vec n a -> Vec ('S n) a
append :: Vec m a -> Vec n a -> Vec (Plus m n) a
append Nil ys = ys
append (Cons x xs) ys = Cons x (append xs ys)
Note that many/most interesting applications of type families require UndecidableInstances
. You should not be scared of this extension.
Another useful sort of type family is one associated with a class. For a really contrived example,
class Box b where
type Elem b :: *
elem :: b -> Elem b
An instance of Box
is a type that something can be pulled out of. For instance,
instance Box (Identity x) where
type Elem (Identity x) = x
elem = runIdentity
instance Box Char where
type Elem Char = String
elem c = [c]
Now elem (Identity 3) = 3
and elem 'x' = "x"
.
You can also use type families to make weird skolem variables. This is best done in the as-yet-unreleased GHC 8.0.1, where it will look like
type family Any :: k where {}
Any
is a peculiar type. It's uninhabited, it can't be (specifically) an instance of a class, and it's poly-kinded. This turns out to be really useful for certain purposes. This particular type is advertised as a safe target for unsafeCoerce
, but Data.Constraint.Forall
uses similar type families for more interesting purposes.