liveBook · Manning

Heterogeneous data structure

As explained in Learn Haskell by Example

A heterogeneous list is a list whose elements have different concrete types while sharing a common existential interface.

A heterogeneous data structure stores values with different concrete types while presenting a common interface. In Haskell, existential quantification hides each value’s type parameter, allowing one list to contain values such as frequencies, chromatic notes, and semitones. section 11.0 section 11.1

Build a useful heterogeneous type by following three design steps:

  • Wrap values in an existential constructor such as `Pitch`, hiding the underlying type while retaining required constraints such as `Pitchable` and `Show`. section 11.1.1 section 11.1.2
  • Define operations through the shared interface. A `Pitchable Pitch` instance can delegate `toFrequency` to the wrapped value. section 11.1.2
  • Use the wrapper in a recursive structure. Replacing a type parameter with `Pitch` lets notes, pauses, sequences, and groups share one composition type. section 11.1.2 section 11.1

Mixed musical pitches

A list can contain a chromatic note, a frequency, and a semitone value: `pitches = [Pitch (a 4), Pitch (440 :: Hz), Pitch (0 :: Semitone)]`. Its type is `[Pitch]`, and mapping `toFrequency` produces `[440.0, 440.0, 440.0]`. The values differ internally but support the same operation. section 11.1.2

Listing 11.1 A data type for structuring notes
data Pitch = forall a. (Pitchable a, Show a) => Pitch a  #1

deriving instance Show Pitch  #2

instance Pitchable Pitch where
  toFrequency (Pitch p) = toFrequency p  #3

Inspect the existential `Pitch` definition, its constraints, derived display support, and delegated `Pitchable` instance. section 11.1.2

ghci> :set -XExistentialQuantification
ghci> data E = forall a. MkE a
ghci> :t MkE
MkE :: a -> E
ghci> :t MkE (1 :: Int)
MkE (1 :: Int) :: E
ghci> :t MkE (1 :: Float)
MkE (1 :: Float) :: E
ghci> :t [MkE (1 :: Int), MkE (2 :: Float)]
[MkE (1 :: Int), MkE (2 :: Float)] :: [E]

Compare ordinary parametrized lists with an existential constructor in GHCi, including the type error and the successful mixed list. section 11.1.1

How Learn Haskell by Example covers it

How deep it goes
Defines existential wrapping, constraints, instances, and nested composition use, but does not develop existential types beyond these implementations.
Examples
Uses mixed pitch lists, `Pitch`, recursive `NoteStructure` values, and GHCi comparisons between homogeneous and existential lists.
What it assumes
Assumes basic Haskell lists, parametrized data types, and type classes while teaching existential quantification through concrete examples.
What you can do after
Build it from scratch by hiding type parameters, constraining permitted operations, and composing differently typed values through one interface.
How it connects
Connects existential quantification with type constraints, type classes, polymorphic data structures, interpreters, and domain-specific languages.

Related concepts

This explainer was generated by Manning's knowledge graph from the text of Learn Haskell by Example. The section links point to the parts of the book it draws on.