6.2. Instance Declarations🔗

The syntax of instance declarations is almost identical to that of definitions. The only syntactic differences are that the keyword Lean.Parser.Command.declaration : commanddef is replaced by Lean.Parser.Command.declaration : commandinstance and the name is optional:

syntax

Most instances define each method using Lean.Parser.Command.declaration : commandwhere syntax:

instance ::= ...
    | `attrKind` matches `("scoped" <|> "local")?`, used before an attribute like `@[local simp]`. instance ((priority := prio))? `declId` matches `foo` or `foo.{u,v}`: an identifier possibly followed by a list of universe names declId? `declSig` matches the signature of a declaration with required type: a list of binders and then `: type` declSig where
        structInstField*

However, type classes are inductive types, so instances can be constructed using any expression with an appropriate type:

instance ::= ...
    | `attrKind` matches `("scoped" <|> "local")?`, used before an attribute like `@[local simp]`. instance ((priority := prio))? `declId` matches `foo` or `foo.{u,v}`: an identifier possibly followed by a list of universe names declId? `declSig` matches the signature of a declaration with required type: a list of binders and then `: type` declSig :=
        termTermination hints are `termination_by` and `decreasing_by`, in that order.

Instances may also be defined by cases; however, this feature is rarely used outside of Decidable instances:

instance ::= ...
    | `attrKind` matches `("scoped" <|> "local")?`, used before an attribute like `@[local simp]`. instance ((priority := prio))? `declId` matches `foo` or `foo.{u,v}`: an identifier possibly followed by a list of universe names declId? `declSig` matches the signature of a declaration with required type: a list of binders and then `: type` declSig
        (| term => term)*Termination hints are `termination_by` and `decreasing_by`, in that order.

Instances defined with explicit terms often consist of either anonymous constructors (Lean.Parser.Term.anonymousCtor : termThe *anonymous constructor* `⟨e, ...⟩` is equivalent to `c e ...` if the expected type is an inductive type with a single constructor `c`. If more terms are given than `c` has parameters, the remaining arguments are turned into a new anonymous constructor application. For example, `⟨a, b, c⟩ : α × (β × γ)` is equivalent to `⟨a, ⟨b, c⟩⟩`. ⟨...⟩) wrapping method implementations or of invocations of inferInstanceAs on definitionally equal types.

Elaboration of instances is almost identical to the elaboration of ordinary definitions, with the exception of the caveats documented below. If no name is provided, then one is created automatically. It is possible to refer to this generated name directly, but the algorithm used to generate the names has changed in the past and may change in the future. It's better to explicitly name instances that will be referred to directly. After elaboration, the new instance is registered as a candidate for instance search. Adding the attribute instance to a name can be used to mark any other defined name as a candidate.

Instance Name Generation

Following these declarations:

structure NatWrapper where val : Nat instance : BEq NatWrapper where beq | x, y => x == y

the name instBEqNatWrapper refers to the new instance.

Variations in Instance Definitions

Given this structure type:

structure NatWrapper where val : Nat

all of the following ways of defining a BEq instance are equivalent:

instance : BEq NatWrapper where beq | x, y => x == y instance : BEq NatWrapper := fun x y => x.val == y.val instance : BEq NatWrapper := fun x y => x == y

Aside from introducing different names into the environment, the following are also equivalent:

@[instance] def instBeqNatWrapper : BEq NatWrapper where beq | x, y => x == y instance : BEq NatWrapper := fun x y => x.val == y.val instance : BEq NatWrapper := fun x y => x == y

6.2.1. Recursive Instances🔗

Functions defined in Lean.Parser.Command.declaration : commandwhere structure definition syntax are not recursive. Because instance declaration is a version of structure definition, type class methods are also not recursive by default. Instances for recursive inductive types are common, however. There is a standard idiom to work around this limitation: define a recursive function independently of the instance, and then refer to it in the instance definition. By convention, these recursive functions have the name of the corresponding method, but are defined in the type's namespace.

Instances are not recursive

Given this definition of NatTree:

inductive NatTree where | leaf | branch (left : NatTree) (val : Nat) (right : NatTree)

the following BEq instance fails:

instance : BEq NatTree where beq | .leaf, .leaf => true | .branch l1 v1 r1, .branch l2 v2 r2 => failed to synthesize BEq NatTree Additional diagnostic information may be available using the `set_option diagnostics true` command.l1 == l2 && v1 == v2 && failed to synthesize BEq NatTree Additional diagnostic information may be available using the `set_option diagnostics true` command.r1 == r2 | _, _ => false

with errors in both the left and right recursive calls that read:

failed to synthesize
  BEq NatTree
Additional diagnostic information may be available using the `set_option diagnostics true` command.

Given a suitable recursive function, such as NatTree.beq:

def NatTree.beq : NatTree NatTree Bool | .leaf, .leaf => true | .branch l1 v1 r1, .branch l2 v2 r2 => l1 == l2 && v1 == v2 && r1 == r2 | _, _ => false

the instance can be created in a second step:

instance : BEq NatTree where beq := NatTree.beq

or, equivalently, using anonymous constructor syntax:

instance : BEq NatTree := NatTree.beq

Furthermore, instances are not available for instance synthesis during their own definitions. They are first marked as being available for instance synthesis after they are defined. Nested inductive types, in which the recursive occurrence of the type occurs as a parameter to some other inductive type, may require an instance to be available to write even the recursive function. The standard idiom to work around this limitation is to create a local instance in a recursively-defined function that includes a reference to the function being defined, taking advantage of the fact that instance synthesis may use every binding in the local context with the right type.

Instances for nested types

In this definition of NatRoseTree, the type being defined occurs nested under another inductive type constructor (Array):

inductive NatRoseTree where | node (val : Nat) (children : Array NatRoseTree)

Checking the equality of rose trees requires checking equality of arrays. However, instances are not typically available for instance synthesis during their own definitions, so the following definition fails, even though NatRoseTree.beq is a recursive function and is in scope in its own definition.

def NatRoseTree.beq : (tree1 tree2 : NatRoseTree) Bool | .node val1 children1, .node val2 children2 => val1 == val2 && failed to synthesize BEq (Array NatRoseTree) Additional diagnostic information may be available using the `set_option diagnostics true` command.children1 == children2
failed to synthesize
  BEq (Array NatRoseTree)
Additional diagnostic information may be available using the `set_option diagnostics true` command.

To solve this, a local BEq NatRoseTree instance may be let-bound:

partial def NatRoseTree.beq : (tree1 tree2 : NatRoseTree) Bool | .node val1 children1, .node val2 children2 => let _ : BEq NatRoseTree := NatRoseTree.beq val1 == val2 && children1 == children2

The use of array equality on the children finds the let-bound instance during instance synthesis.

6.2.2. Instances of class inductives🔗

Many instances have function types: any instance that itself recursively invokes instance search is a function, as is any instance with implicit parameters. While most instances only project method implementations from their own instance parameters, instances of class inductive types typically pattern-match one or more of their arguments, allowing the instance to select the appropriate constructor. This is done using ordinary Lean function syntax. Just as with other instances, the function in question is not available for instance synthesis in its own definition.

An instance for a sum class

Because DecidableEq α is an abbreviation for (a b : α) Decidable (Eq a b), its arguments can be used directly, as in this example:

inductive ThreeChoices where | yes | no | maybe instance : DecidableEq ThreeChoices | .yes, .yes => .isTrue rfl | .no, .no => .isTrue rfl | .maybe, .maybe => .isTrue rfl | .yes, .maybe | .yes, .no | .maybe, .yes | .maybe, .no | .no, .yes | .no, .maybe => .isFalse nofun
A recursive instance for a sum class

The type StringList represents monomorphic lists of strings:

inductive StringList where | nil | cons (hd : String) (tl : StringList)

In the following attempt at defining a DecidableEq instance, instance synthesis invoked while elaborating the inner termIfThenElse : term`if c then t else e` is notation for `ite c t e`, "if-then-else", which decides to return `t` or `e` depending on whether `c` is true or false. The explicit argument `c : Prop` does not have any actual computational content, but there is an additional `[Decidable c]` argument synthesized by typeclass inference which actually determines how to evaluate `c` to true or false. Write `if h : c then t else e` instead for a "dependent if-then-else" `dite`, which allows `t`/`e` to use the fact that `c` is true/false. if fails because the instance is not available for instance synthesis in its own definition:

instance : DecidableEq StringList | .nil, .nil => .isTrue rfl | .cons h1 t1, .cons h2 t2 => if h : h1 = h2 then failed to synthesize Decidable (t1 = t2) Additional diagnostic information may be available using the `set_option diagnostics true` command.if h' : t1 = t2 then .isTrue (h1:Stringt1:StringListh2:Stringt2:StringListh:h1 = h2h':t1 = t2StringList.cons h1 t1 = StringList.cons h2 t2 All goals completed! 🐙) else .isFalse (h1:Stringt1:StringListh2:Stringt2:StringListh:h1 = h2h':¬t1 = t2¬StringList.cons h1 t1 = StringList.cons h2 t2 h1:Stringt1:StringListh2:Stringt2:StringListh:h1 = h2h':¬t1 = t2hEq:StringList.cons h1 t1 = StringList.cons h2 t2False; h1:Stringt1:StringListh:h1 = h1h':¬t1 = t1False; All goals completed! 🐙) else .isFalse (h1:Stringt1:StringListh2:Stringt2:StringListh:¬h1 = h2¬StringList.cons h1 t1 = StringList.cons h2 t2 h1:Stringt1:StringListh2:Stringt2:StringListh:¬h1 = h2hEq:StringList.cons h1 t1 = StringList.cons h2 t2False; h1:Stringt1:StringListh:¬h1 = h1False; All goals completed! 🐙) | .nil, .cons _ _ | .cons _ _, .nil => .isFalse nofun
failed to synthesize
  Decidable (t1 = t2)
Additional diagnostic information may be available using the `set_option diagnostics true` command.

However, because it is an ordinary Lean function, it can recursively refer to its own explicitly-provided name:

instance instDecidableEqStringList : DecidableEq StringList | .nil, .nil => .isTrue rfl | .cons h1 t1, .cons h2 t2 => if h : h1 = h2 then if h' : application type mismatch @dite ?m.74 (instDecidableEqStringList t1 t2) argument instDecidableEqStringList t1 t2 has type Decidable (t1 = t2) : Type but is expected to have type Prop : TypeinstDecidableEqStringList t1 t2 then .isTrue (h1:Stringt1:StringListh2:Stringt2:StringListh:h1 = h2h':sorryStringList.cons h1 t1 = StringList.cons h2 t2 h1:Stringt1:StringListh2:Stringt2:StringListh:h1 = h2h':sorryt1 = t2) else .isFalse (h1:Stringt1:StringListh2:Stringt2:StringListh:h1 = h2h':¬sorry¬StringList.cons h1 t1 = StringList.cons h2 t2 h1:Stringt1:StringListh2:Stringt2:StringListh:h1 = h2h':¬sorryhEq:StringList.cons h1 t1 = StringList.cons h2 t2False; h1:Stringt1:StringListh':¬sorryh:h1 = h1False; h1:Stringt1:StringListh':¬sorryh:h1 = h1False) else .isFalse (h1:Stringt1:StringListh2:Stringt2:StringListh:¬h1 = h2¬StringList.cons h1 t1 = StringList.cons h2 t2 h1:Stringt1:StringListh2:Stringt2:StringListh:¬h1 = h2hEq:StringList.cons h1 t1 = StringList.cons h2 t2False; h1:Stringt1:StringListh:¬h1 = h1False; All goals completed! 🐙) | .nil, .cons _ _ | .cons _ _, .nil => .isFalse nofun

6.2.3. Instance Priorities🔗

Instances may be assigned priorities. During instance synthesis, higher-priority instances are preferred; see the section on instance synthesis for details of instance synthesis.

syntax

Priorities may be numeric:

prio ::=
    num

If no priority is specified, the default priority that corresponds to 1000 is used:

prio ::= ...
    | The default priority `default = 1000`, which is used when no priority is set. default

Three named priorities are available when numeric values are too fine-grained, corresponding to 100, 500, and 10000 respectively. The prioMid : prioThe standardized "medium" priority `mid = 500`. This is lower than `default`, and higher than `low`. mid priority is lower than prioDefault : prioThe default priority `default = 1000`, which is used when no priority is set. default.

prio ::= ...
    | The standardized "low" priority `low = 100`, for things that should be lower than default priority. low
prio ::= ...
    | The standardized "medium" priority `mid = 500`. This is lower than `default`, and higher than `low`.
mid
prio ::= ...
    | The standardized "high" priority `high = 10000`, for things that should be higher than default priority. high

Finally, priorities can be added and subtracted, so default + 2 is a valid priority, corresponding to 1002:

prio ::= ...
    | Parentheses are used for grouping priority expressions. (prio)
prio ::= ...
    | Addition of priorities. This is normally used only for offsetting, e.g. `default + 1`. prio + prio
prio ::= ...
    | Subtraction of priorities. This is normally used only for offsetting, e.g. `default - 1`. prio - prio

6.2.4. Default Instances🔗

The default_instance attribute specifies that an instance should be used as a fallback in situations where there is not enough information to select it otherwise. If no priority is specified, then the default priority default is used.

syntax
attr ::= ...
    | default_instance prio?
Default Instances

A default instance of OfNat Nat is used to select Nat for natural number literals in the absence of other type information. It is declared in the Lean standard library with priority 100. Given this representation of even numbers, in which an even number is represented by half of it:

structure Even where half : Nat

the following instances allow numeric literals to be used for small Even values (a limit on the depth of type class instance search prevents them from being used for arbitrarily large literals):

instance ofNatEven0 : OfNat Even 0 where ofNat := 0 instance ofNatEvenPlusTwo [OfNat Even n] : OfNat Even (n + 2) where ofNat := (OfNat.ofNat n : Even).half + 1 { half := 0 }#eval (0 : Even) { half := 17 }#eval (34 : Even) { half := 127 }#eval (254 : Even)
{ half := 0 }
{ half := 17 }
{ half := 127 }

Specifying them as default instances with a priority greater than or equal to 100 causes them to be used instead of Nat:

attribute [default_instance 100] ofNatEven0 attribute [default_instance 100] ofNatEvenPlusTwo { half := 0 }#eval 0 { half := 17 }#eval 34
{ half := 0 }
{ half := 17 }

Non-even numerals still use the OfNat Nat instance:

5#eval 5
5

6.2.5. The Instance Attribute🔗

The instance attribute declares a name to be an instance, with the specified priority. Like other attributes, instance can be applied globally, locally, or only when the current namespace is opened. The Lean.Parser.Command.declaration : commandinstance declaration is a form of definition that automatically applies the instance attribute.

syntax

Declares the definition to which it is applied to be an instance. If no priority is provided, then the default priority default is used.

attr ::= ...
    | instance prio?