Type checking is the primary feedback mechanism in Lean. Before evaluating or proving anything, you should know the exact type of each expression. The command #check asks Lean to infer and display that type.
Problem
You want to understand what an expression means in Lean, diagnose type errors, and inspect functions, theorems, and intermediate terms.
Solution
Use #check on any expression.
#check 5
#check "Lean"
#check true
#check NatTypical output:
5 : Nat
"Lean" : String
true : Bool
Nat : Type#check does not evaluate the expression. It only reports its type.
Checking Functions
#check Nat.succ
#check (fun x : Nat => x + 1)Output:
Nat.succ : Nat -> Nat
fun x => x + 1 : Nat -> NatThis tells you how to apply the function.
Checking Applications
#check Nat.succ 3
#check (fun x : Nat => x + 1) 10Output:
Nat.succ 3 : Nat
(fun x => x + 1) 10 : Nat#check confirms both the validity and the result type.
Checking Definitions
def double (n : Nat) : Nat :=
n + n
#check double
#check double 5Output:
double : Nat -> Nat
double 5 : NatUse this to confirm that a definition has the intended type.
Checking Theorems
theorem id_prop (P : Prop) : P -> P := by
intro h
exact h
#check id_prop
#check id_prop TrueOutput:
id_prop : (P : Prop) -> P -> P
id_prop True : True -> TrueA theorem behaves like a function whose inputs are proofs and whose output is a proof.
Inspecting Implicit Arguments
Many arguments are implicit. Use #check to see how Lean presents them.
def idImplicit {α : Type} (x : α) : α :=
x
#check idImplicitOutput:
idImplicit : {α : Type} -> α -> αTo see all arguments explicitly:
#check @idImplicitOutput:
@idImplicit : (α : Type) -> α -> αThe @ form exposes implicit parameters.
Checking Overloaded Notation
Operators are often overloaded. Use #check to resolve them.
#check (2 + 3)
#check (2 : Nat) + (3 : Nat)
#check (2 : Int) + (3 : Int)Each expression may refer to a different underlying function depending on the type.
Checking Types Themselves
Types are also expressions.
#check Nat
#check Nat -> Nat
#check List Nat
#check PropOutput:
Nat : Type
Nat -> Nat : Type
List Nat : Type
Prop : TypeThis confirms that Lean treats types uniformly.
Checking Partial Expressions
You can use _ as a placeholder.
#check (fun x : Nat => _)Lean reports the expected type for the hole. This is useful when constructing expressions incrementally.
Checking in Context
Inside a proof or definition, #check can inspect local variables.
theorem example (P Q : Prop) (hp : P) (hpq : P -> Q) : Q := by
#check hp
#check hpq
exact hpq hpThis reveals:
hp : P
hpq : P -> QUse this to understand the current context.
Checking Structures and Fields
structure Point where
x : Nat
y : Nat
#check Point
#check Point.x
#check Point.yOutput:
Point : Type
Point.x : Point -> Nat
Point.y : Point -> NatThis shows how to access structure fields.
Checking Typeclass-Driven Expressions
#check (0 : Nat)
#check (0 : Int)Numeric literals depend on typeclass instances. #check confirms which instance is used.
#check vs #eval
| Command | Purpose |
|---|---|
#check | show type |
#eval | compute value |
Example:
#check 2 + 3
#eval 2 + 3Output:
2 + 3 : Nat
5Use #check first to ensure the expression is well-typed, then #eval to compute it.
Recipe: Diagnose a Type Error
If Lean reports a type mismatch:
- Check subexpressions
#check subexpr- Add type annotations
#check (subexpr : ExpectedType)- Inspect function types
#check function- Expand implicit arguments
#check @functionThis isolates where the mismatch occurs.
Recipe: Explore a Library Function
When using a new function:
#check List.map
#check List.map (fun x : Nat => x + 1)Then apply it:
#eval List.map (fun x : Nat => x + 1) [1, 2, 3]This pattern avoids guessing argument order and types.
Common Pitfalls
If #check fails, the expression is ill-typed.
If the type is more general than expected, implicit arguments may be involved.
If the type is ambiguous, add a type ascription.
If the result is hard to read, use #check @name to expose all parameters.
If names are not found, verify imports and namespaces.
Takeaway
#check is the primary tool for understanding Lean code. It reveals types, clarifies implicit arguments, and exposes how expressions are interpreted. Regular use of #check reduces trial and error and makes development systematic.