liveBook · Manning

Type expression

As explained in Learn Haskell by Example

A type expression is syntactic notation that describes the type of a value or expression, including the element type of a list or the component types of a tuple.

A type expression is Haskell notation that states the type associated with a name or value-producing expression. A top-level type signature uses `::`, as in `name :: type`, and is commonly placed beside the corresponding definition. Haskell can infer many types, but explicit signatures document intended values and constrain how expressions are used. section 2.2 section 2.6

name :: type

A basic signature maps an identifier to its type; the definition may follow it. section 2.2

Atomic type names are themselves type expressions. Common choices include `Bool`, `Char`, `Int`, `Integer`, `Float`, and `Double`. A signature can resolve otherwise ambiguous numeric expressions: declaring `nano :: Double` requires its value to be a `Double`, while `mega :: Int` keeps its calculation in `Int`. section 2.2.1

Type expressions describe structured values as well as atomic ones:

  • A list type brackets its element type: `[Int]` is a list whose elements must be `Int`; `[Double]` similarly directs inference for every element, including expressions inside the list. section 2.2.2
  • A tuple type lists component types in parentheses: `(Int, Float)` describes a pair, while `(Int, Char, Double)` describes a three-part tuple. section 2.2.2
  • A function type uses `->`: `(t -> a) -> [t] -> [a]` describes a function that accepts a converter and a list, then returns a list of converted values. section 2.5.2
intList :: [Int]
intList = [1, 2, 3]

boolList :: [Bool]
boolList = [True]

floatList :: [Float]
floatList = []

doubleList :: [Double]
doubleList = [1, 1 + 1, 1 + 2]

These signatures show list element types, including an empty list with a declared `Float` element type. section 2.2.2

Type variables such as `t` and `a` make a signature parametric: each occurrence of the same variable must be replaced consistently, but different variables may stand for different types. For `transform :: (t -> a) -> [t] -> [a]`, using an `Int -> String` converter makes the input `[Int]` and the result `[String]`. section 2.5.2

A signature can reveal a missing requirement

A proposed lookup signature, `member :: a -> [(a, b)] -> Bool`, describes keys and searched values with the same type. But using `(==)` requires comparable keys, so compilation reports a missing `Eq a` instance. The type expression must account for the operations used by the implementation. section 5.1.2

ghci> :type transform
transform :: (t -> a) -> [t] -> [a]

GHCi can inspect a polymorphic function’s inferred type before concrete types are substituted. section 2.5.2

How Learn Haskell by Example covers it

How deep it goes
Explains atomic, list, tuple, function, and parametric forms, then defers restricted type variables to type classes.
Examples
Uses numeric prefixes, typed lists and tuples, `transform`, and an associative-list `member` function.
What it assumes
Introduces atomic and collection type forms before applying signatures to functions and data structures.
What you can do after
Evaluate/understand signatures for concrete collections, tuple components, polymorphic functions, and comparison-related compiler errors.
How it connects
Connects type expressions to type inference, lists, tuples, parametric polymorphism, higher-order functions, and type classes.

Also explained in

Compare how 2 books explain type expression →

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.