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















