Skip to content

Haskell Type Families Guide — Type-Level Functions and Associated Types

DodaTech Updated 2026-06-28 3 min read

In this tutorial, you will learn about Haskell Type Families Guide. We cover key concepts, practical examples, and best practices to help you master this topic.

Haskell type families are type-level functions that map types to types -- open families for extensible mappings (TypeFamily), closed families for exhaustive pattern matching, and associated types within typeclasses for type-indexed data families.

Open Type Families

{-# LANGUAGE TypeFamilies #-}

-- Open type family (extensible)
type family Elem container :: *

-- Instances
type instance Elem [a] = a
type instance Elem (Maybe a) = a
type instance Elem (Map k v) = v

-- Usage
first :: container -> Elem container -> Bool
-- first xs x = ... checks if x is first element

Closed Type Families

{-# LANGUAGE TypeFamilies #-}

-- Closed family (exhaustive matching)
type family TypeSize a where
    TypeSize Int     = 4
    TypeSize Double  = 8
    TypeSize Bool    = 1
    TypeSize a       = 0  -- default case

-- Type-level computation
type family Add a b where
    Add Zero     b = b
    Add (Succ a) b = Succ (Add a b)

Associated Type Families

{-# LANGUAGE TypeFamilies #-}

class Collection c where
    type Item c        -- associated type
    insert :: Item c -> c -> c
    member :: Item c -> c -> Bool

instance Collection [a] where
    type Item [a] = a
    insert x xs = x : xs
    member x xs = elem x xs

instance Collection (Set a) where
    type Item (Set a) = a
    insert x s = Set.insert x s
    member x s = Set.member x s

Data Families

{-# LANGUAGE TypeFamilies #-}

-- Data family: different data structure per type
data family Array a

data instance Array Int    = IArray (Vector Int)
data instance Array Double = DArray (Vector Double)
data instance Array Bool   = BArray (Vector Bool)

-- Usage
createArray :: Array a -> Int -> IO ()
-- dispatches to correct implementation

Common Mistakes

1. Overlapping instances

Open type family instances must not overlap. Closed families can overlap with order-based resolution.

2. Instance resolution order

Closed families try instances top-to-bottom. Put more specific patterns first.

3. Type family vs functional dependency

Both associate types with other types. Type families are more expressive. Functional dependencies are simpler.

Practice Questions

1. What is the difference between open and closed type families? Open families allow new instances anywhere. Closed families define all instances in one block.

2. What do associated type families enable? Type-indexed type mappings within typeclasses. Each instance defines its own auxiliary type.

3. What is a data family? Similar to a type family but defines type constructors (data types) rather than type aliases.

FAQ

{{< faq question="Can type families be recursive?" >}} Yes. Closed type families support Recursion: type family Fact n where { Fact 0 = 1; Fact n = n * Fact (n-1) }. {{< /faq >}}

{{< faq question="What is injectivity?" >}} A type family can be declared injective: type family F a = r | r -> a. This enables better type inference. {{< /faq >}}

{{< faq question="Are type families computed at compile time?" >}} Yes. Type families are fully evaluated during Type Checking. There is no runtime cost. {{< /faq >}}

What's Next

Now learn about Template Haskell.

Topic Description Link
Template Haskell Meta-programming {{< ref "30-template-haskell" >}}
Julia Start learning Julia Julia

Built by the developers of DodaTech

Doda Browser, DodaZIP & Durga Antivirus Pro