5.5. Literals

There are two kinds of numeric literal: natural number literals and scientific literals. Both are overloaded via type classes.

5.5.1. Natural Numbers

When Lean encounters a natural number literal n, it interprets it via the overloaded method OfNat.ofNat n. A default instance of OfNat Nat n ensures that the type Nat can be inferred when no other type information is present.

🔗type class
OfNat.{u} (α : Type u) : NatType u

The class OfNat α n powers the numeric literal parser. If you write 37 : α, Lean will attempt to synthesize OfNat α 37, and will generate the term (OfNat.ofNat 37 : α).

There is a bit of infinite regress here since the desugaring apparently still contains a literal 37 in it. The type of expressions contains a primitive constructor for "raw natural number literals", which you can directly access using the macro nat_lit 37. Raw number literals are always of type Nat. So it would be more correct to say that Lean looks for an instance of OfNat α (nat_lit 37), and it generates the term (OfNat.ofNat (nat_lit 37) : α).

Instance Constructor

OfNat.mk.{u}

Methods

ofNat : α

The OfNat.ofNat function is automatically inserted by the parser when the user writes a numeric literal like 1 : α. Implementations of this typeclass can therefore customize the behavior of n : α based on n and α.

Custom Natural Number Literals

The structure NatInterval represents an interval of natural numbers.

structure NatInterval where low : Nat high : Nat low_le_high : low high instance : Add NatInterval where add | lo1, hi1, le1, lo2, hi2, le2 => lo1 + lo2, hi1 + hi2, lo1:Nathi1:Natle1:lo1hi1lo2:Nathi2:Natle2:lo2hi2lo1 + lo2hi1 + hi2 All goals completed! 🐙

An OfNat instance allows natural number literals to be used to represent intervals:

instance : OfNat NatInterval n where ofNat := n, n, n:Natnn All goals completed! 🐙 { low := 8, high := 8, low_le_high := _ }#eval (8 : NatInterval)
{ low := 8, high := 8, low_le_high := _ }

There are no separate integer literals. Terms such as -5 consist of a prefix negation (which can be overloaded via the Neg type class) applied to a natural number literal.

5.5.2. Scientific Numbers

Scientific number literals consist of a sequence of digits followed by an optional period and decimal part and an optional exponent. If no period or exponent is present, then the term is instead a natural number literal. Scientific numbers are overloaded via the OfScientific type class.

🔗type class
OfScientific.{u} (α : Type u) : Type u

For decimal and scientific numbers (e.g., 1.23, 3.12e10). Examples:

  • 1.23 is syntax for OfScientific.ofScientific (nat_lit 123) true (nat_lit 2)

  • 121e100 is syntax for OfScientific.ofScientific (nat_lit 121) false (nat_lit 100)

Note the use of nat_lit; there is no wrapping OfNat.ofNat in the resulting term.

Instance Constructor

OfScientific.mk.{u}

Methods

ofScientific : NatBoolNatα

There is an OfScientific instance for Float, but no separate floating-point literals.

5.5.3. Strings

String literals are described in the chapter on strings.

5.5.4. Lists and Arrays

List and array literals contain comma-separated sequences of elements inside of brackets, with arrays prefixed by a hash mark (#). Array literals are interpreted as list literals wrapped in a call to a conversion. For performance reasons, very large list and array literals are converted to sequences of local definitions, rather than just iterated applications of the list constructor.

syntaxList Literals
term ::= ...
    | The syntax `[a, b, c]` is shorthand for `a :: b :: c :: []`, or
`List.cons a (List.cons b (List.cons c List.nil))`. It allows conveniently constructing
list literals.

For lists of length at least 64, an alternative desugaring strategy is used
which uses let bindings as intermediates as in
`let left := [d, e, f]; a :: b :: c :: left` to avoid creating very deep expressions.
Note that this changes the order of evaluation, although it should not be observable
unless you use side effecting operations like `dbg_trace`.
[term,*]
syntaxArray Literals
term ::= ...
    | #[term,*]
Long List Literals

This list contains 32 elements. The generated code is an iterated application of List.cons:

[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] : List Nat#check [1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1]
[1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1, 1] : List Nat

With 33 elements, the list literal becomes a sequence of local definitions:

let y := let y := let y := [1, 1, 1, 1, 1]; 1 :: 1 :: 1 :: 1 :: y; let y := 1 :: 1 :: 1 :: 1 :: y; 1 :: 1 :: 1 :: 1 :: y; let y := let y := 1 :: 1 :: 1 :: 1 :: y; 1 :: 1 :: 1 :: 1 :: y; let y := 1 :: 1 :: 1 :: 1 :: y; 1 :: 1 :: 1 :: 1 :: y : List Nat#check [1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1,1,1,1,1,1,1,1, 1]
let y :=
  let y :=
    let y := [1, 1, 1, 1, 1];
    1 :: 1 :: 1 :: 1 :: y;
  let y := 1 :: 1 :: 1 :: 1 :: y;
  1 :: 1 :: 1 :: 1 :: y;
let y :=
  let y := 1 :: 1 :: 1 :: 1 :: y;
  1 :: 1 :: 1 :: 1 :: y;
let y := 1 :: 1 :: 1 :: 1 :: y;
1 :: 1 :: 1 :: 1 :: y : List Nat