Skip to content
AgileLogics
4 min read

Type Inference

Prerequisites: Data types

F# is statically typed, yet you rarely write types. The compiler infers them from how values are used:

let width = 12          // int, because 12 is an int
let area w h = w * h    // int -> int -> int, because * defaults to int
let greet name = "Hello, " + name   // string -> string

printfn "%d" (area width 3)
printfn "%s" (greet "Ada")
36
Hello, Ada

In an editor with Ionide or Rider, hover over any name to see its inferred type — or turn on inline type hints.

Annotations

Add a type annotation with name: type when you want to document intent or when the compiler needs help:

let half (x: float) : float = x / 2.0
let shout (text: string) = text.ToUpper() + "!"

printfn "%.1f" (half 5.0)
printfn "%s" (shout "hello")
2.5
HELLO!

The annotation after the parameters (: float) is the return type. In shout, the annotation is required: the compiler reads code top to bottom and needs to know text is a string before it can look up the ToUpper method.

When inference needs help

let shout text = text.ToUpper()
main.fsx(1,18): error FS0072: The type of this expression could not be inferred before accessing its members. Add a type annotation, e.g. (expr: SomeType), to constrain the type before this point.

Method calls with . (like .ToUpper()) need a known type. Plain functions and operators don't, which is one reason idiomatic F# prefers functions such as String.length s over methods in pipelines.

Generic by default

If nothing constrains a type, F# makes the function generic:

let pair a b = (a, b)
let first (a, _) = a

printfn "%A" (pair 1 "one")
printfn "%A" (pair true 2.5)
printfn "%s" (first ("left", 99))
(1, "one")
(true, 2.5)
left

pair has the type 'a -> 'b -> 'a * 'b — 'a and 'b (read "alpha" and "beta") are type parameters that stand for any type. You'll learn more about generics in Level 2.

  • Let inference do the work; annotate function parameters in public APIs, for method calls, and wherever it makes code clearer.
  • When a type error is confusing, adding an annotation often moves the error to the line that's actually wrong.