liveBook · Manning

Type inference

As explained in Learn Haskell by Example

Type inference is the automatic determination of expression and variable types from their usage and surrounding context.

Type inference lets Haskell determine the types of functions, variables, and expressions from how they are used. Haskell is statically typed: types are known at compile time and do not change while a program runs. In many cases, definitions work without written type annotations because the compiler can infer them from context. section 2.2 section 2.2.1

Explicit type signatures still matter for top-level definitions. A signature associates a name with a type, while the following definition supplies its value. They document intended use and resolve cases where inference needs help. For example, choosing Int for a cipher-wheel key expresses that rotations are discrete fixed-size integers, while a message can be represented as a collection of Char values. section 2.2 section 2.2.1

Inference follows the constraints imposed by surrounding types:

  • A numeric literal can be ambiguous, but an expression used where an Int is required is inferred as Int; operations such as exponentiation keep compatible numeric types. section 2.2.1
  • Every list element must share one type. Declaring a value as [Double] makes literals and expressions inside that list Double values, even when a literal such as 1 could otherwise be Int. section 2.2.2
  • Tuples have fixed length but may combine types, so a tuple type records the type of each position rather than requiring uniform elements. section 2.2.2
  • Type variables support parametric polymorphism. When a polymorphic function is given an Int -> String function, inference can specialize its input list to [Int] and result to [String]. section 2.5.2

A Double list constrains its elements

A signature can guide inference throughout a definition. In doubleList :: [Double], each element must evaluate to Double, so 1, 1 + 1, and 1 + 2 are accepted as Double expressions. The declared list type supplies the context that resolves the numeric literals. section 2.2.2

ghci> :{
ghci| f :: Int -> String
ghci| f n = show n
ghci| :}
ghci> g xs = transform f xs
ghci> :t g
g :: [Int] -> [String]

Inspecting g after composing transform with f shows inference propagating concrete types through a polymorphic higher-order function: g becomes [Int] -> [String]. section 2.5.2

This specialization is a practical use of parametric polymorphism, comparable to generics in languages such as Java or Go. General type variables let higher-order functions work across types instead of requiring separate versions for each data type. Type classes are deferred for expressing restrictions on those variables. section 2.5.2

How Learn Haskell by Example covers it

How deep it goes
Explains contextual inference for numeric values and collections, then demonstrates polymorphic specialization without covering type-class constraints.
Examples
Uses cipher-wheel keys, numeric prefixes, typed lists and tuples, and a transform function specialized from Int to String.
What it assumes
Teaches from zero with atomic types and type expressions, then builds toward lists, tuples, and type variables.
What you can do after
Evaluate/understand how Haskell deduces concrete expression and function types from annotations, collection types, and polymorphic use.
How it connects
Connects type inference to static typing, type signatures, lists, tuples, parametric polymorphism, higher-order functions, and type classes.

Also explained in

Compare how 16 books explain type inference →

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.