'type family' vs 'data family', in brief?
Categories:
Haskell Type Families vs. Data Families: A Concise Guide

Explore the fundamental differences and use cases of Haskell's type families and data families, powerful features for advanced type-level programming.
Haskell's type families and data families are advanced features that allow for type-level computation and polymorphism. While both extend the capabilities of type classes, they serve distinct purposes and operate differently. Understanding their nuances is crucial for writing flexible and powerful generic code in Haskell. This article will briefly explain each concept, highlight their primary distinctions, and provide examples to clarify their application.
What is a Type Family?
A type family is a type-level function that maps types to types. It allows you to define a type that depends on other types, similar to how a function maps values to values. Type families are declared using the type family
keyword and can be associated with type classes (associated type families) or stand alone (top-level type families). They are particularly useful for defining types that vary based on the type parameters of a data structure or a type class instance.
{-# LANGUAGE TypeFamilies #-}
-- A simple top-level type family
type family Elem a where
Elem [x] = x
Elem (Maybe x) = x
-- An associated type family within a type class
class Collection c where
type Item c :: *
add :: Item c -> c -> c
instance Collection [a] where
type Item [a] = a
add x xs = x : xs
instance Collection (Maybe a) where
type Item (Maybe a) = a
add x _ = Just x
Examples of a top-level and an associated type family in Haskell.
What is a Data Family?
A data family, declared with the data family
keyword, allows you to define a family of data types where the structure of the data type itself can vary based on its type parameters. Unlike type families which compute a type, data families define actual data constructors. They are always associated with type classes (associated data families) or stand alone (top-level data families), similar to type families. Data families are ideal when you need to define different data representations for different type instances.
{-# LANGUAGE TypeFamilies #-}
-- A simple top-level data family
data family GMap k v :: *
data instance GMap Int v = GMapInt (Int -> v)
data instance GMap String v = GMapString [(String, v)]
-- An associated data family within a type class
class Indexable i where
data Key i :: *
lookup :: Key i -> i -> Maybe (Elem i)
instance Indexable [a] where
data Key [a] = ListIndex Int
lookup (ListIndex n) xs = if n >= 0 && n < length xs then Just (xs !! n) else Nothing
instance Indexable (Map.Map k v) where
data Key (Map.Map k v) = MapKey k
lookup (MapKey k) m = Map.lookup k m
Examples of a top-level and an associated data family in Haskell.
Elem
type family used in the Indexable
class's lookup
signature is a common pattern: data families define the structure, and type families define related types.Key Differences and Use Cases
The core distinction lies in what they define: type families define types, while data families define data constructors. This difference dictates their primary use cases. Type families are for type-level computation, allowing a type to be determined by other types. Data families are for defining different concrete data structures based on type parameters. Think of type families as type-level functions and data families as type-level data declarations.
flowchart LR A["Type Family"] --> B{"Maps Types to Types"} B --> C["No Data Constructors"] C --> D["Type-level Computation"] D --> E["Example: `Elem [a]` -> `a`"] F["Data Family"] --> G{"Defines Data Constructors"} G --> H["Structure Varies by Type"] H --> I["Type-level Data Declaration"] I --> J["Example: `data instance GMap Int v`"] style A fill:#f9f,stroke:#333,stroke-width:2px style F fill:#bbf,stroke:#333,stroke-width:2px
Conceptual difference between Type Families and Data Families.
In essence, if you need to compute a type based on some input types, use a type family. If you need to define a new data type whose internal structure depends on its type parameters, use a data family. Both are powerful tools for achieving advanced polymorphism and type safety in Haskell.