Module docstring
{"### getElem? "}
{"### getElem? "}
outOfBounds
definition
[Inhabited α] : α
@[never_extract]
def outOfBounds [Inhabited α] : α :=
panic! "index out of bounds"
outOfBounds_eq_default
theorem
[Inhabited α] : (outOfBounds : α) = default
theorem outOfBounds_eq_default [Inhabited α] : (outOfBounds : α) = default := rfl
GetElem
structure
(coll : Type u) (idx : Type v) (elem : outParam (Type w)) (valid : outParam (coll → idx → Prop))
/--
The classes `GetElem` and `GetElem?` implement lookup notation,
specifically `xs[i]`, `xs[i]?`, `xs[i]!`, and `xs[i]'p`.
Both classes are indexed by types `coll`, `idx`, and `elem` which are
the collection, the index, and the element types.
A single collection may support lookups with multiple index
types. The relation `valid` determines when the index is guaranteed to be
valid; lookups of valid indices are guaranteed not to fail.
For example, an instance for arrays looks like
`GetElem (Array α) Nat α (fun xs i => i < xs.size)`. In other words, given an
array `xs` and a natural number `i`, `xs[i]` will return an `α` when `valid xs i`
holds, which is true when `i` is less than the size of the array. `Array`
additionally supports indexing with `USize` instead of `Nat`.
In either case, because the bounds are checked at compile time,
no runtime check is required.
Given `xs[i]` with `xs : coll` and `i : idx`, Lean looks for an instance of
`GetElem coll idx elem valid` and uses this to infer the type of the return
value `elem` and side condition `valid` required to ensure `xs[i]` yields
a valid value of type `elem`. The tactic `get_elem_tactic` is
invoked to prove validity automatically. The `xs[i]'p` notation uses the
proof `p` to satisfy the validity condition.
If the proof `p` is long, it is often easier to place the
proof in the context using `have`, because `get_elem_tactic` tries
`assumption`.
The proof side-condition `valid xs i` is automatically dispatched by the
`get_elem_tactic` tactic; this tactic can be extended by adding more clauses to
`get_elem_tactic_trivial` using `macro_rules`.
`xs[i]?` and `xs[i]!` do not impose a proof obligation; the former returns
an `Option elem`, with `none` signalling that the value isn't present, and
the latter returns `elem` but panics if the value isn't there, returning
`default : elem` based on the `Inhabited elem` instance.
These are provided by the `GetElem?` class, for which there is a default instance
generated from a `GetElem` class as long as `valid xs i` is always decidable.
Important instances include:
* `arr[i] : α` where `arr : Array α` and `i : Nat` or `i : USize`: does array
indexing with no runtime bounds check and a proof side goal `i < arr.size`.
* `l[i] : α` where `l : List α` and `i : Nat`: index into a list, with proof
side goal `i < l.length`.
-/
class GetElem (coll : Type u) (idx : Type v) (elem : outParam (Type w))
(valid : outParam (coll → idx → Prop)) where
/--
The syntax `arr[i]` gets the `i`'th element of the collection `arr`. If there
are proof side conditions to the application, they will be automatically
inferred by the `get_elem_tactic` tactic.
-/
getElem (xs : coll) (i : idx) (h : valid xs i) : elem
term__[_]
definition
: Lean.TrailingParserDescr✝
term__[_]'_
definition
: Lean.TrailingParserDescr✝
decidableGetElem?
abbrev
[GetElem coll idx elem valid] (xs : coll) (i : idx) [Decidable (valid xs i)] : Option elem
/-- Helper function for implementation of `GetElem?.getElem?`. -/
abbrev decidableGetElem? [GetElem coll idx elem valid] (xs : coll) (i : idx) [Decidable (valid xs i)] :
Option elem :=
if h : valid xs i then some xs[i] else none
GetElem?
structure
(coll : Type u) (idx : Type v) (elem : outParam (Type w))
(valid : outParam (coll → idx → Prop)) extends GetElem coll idx elem valid
@[inherit_doc GetElem]
class GetElem? (coll : Type u) (idx : Type v) (elem : outParam (Type w))
(valid : outParam (coll → idx → Prop)) extends GetElem coll idx elem valid where
/--
The syntax `arr[i]?` gets the `i`'th element of the collection `arr`,
if it is present (and wraps it in `some`), and otherwise returns `none`.
-/
getElem? : coll → idx → Option elem
/--
The syntax `arr[i]!` gets the `i`'th element of the collection `arr`,
if it is present, and otherwise panics at runtime and returns the `default` term
from `Inhabited elem`.
-/
getElem! [Inhabited elem] (xs : coll) (i : idx) : elem :=
match getElem? xs i with | some e => e | none => outOfBounds
term__[_]_?
definition
: Lean.TrailingParserDescr✝
term__[_]_!
definition
: Lean.TrailingParserDescr✝
instGetElem?OfGetElemOfDecidable
instance
[GetElem coll idx elem valid] [∀ xs i, Decidable (valid xs i)] : GetElem? coll idx elem valid
instance (priority := low) [GetElem coll idx elem valid] [∀ xs i, Decidable (valid xs i)] :
GetElem? coll idx elem valid where
getElem? xs i := decidableGetElem? xs i
getElem_congr
theorem
[GetElem coll idx elem valid] {c d : coll} (h : c = d) {i j : idx} (h' : i = j) (w : valid c i) :
c[i] = d[j]'(h' ▸ h ▸ w)
theorem getElem_congr [GetElem coll idx elem valid] {c d : coll} (h : c = d)
{i j : idx} (h' : i = j) (w : valid c i) : c[i] = d[j]'(h' ▸ h ▸ w) := by
cases h; cases h'; rfl
getElem_congr_coll
theorem
[GetElem coll idx elem valid] {c d : coll} {i : idx} {w : valid c i} (h : c = d) : c[i] = d[i]'(h ▸ w)
theorem getElem_congr_coll [GetElem coll idx elem valid] {c d : coll} {i : idx} {w : valid c i}
(h : c = d) : c[i] = d[i]'(h ▸ w) := by
cases h; rfl
getElem_congr_idx
theorem
[GetElem coll idx elem valid] {c : coll} {i j : idx} {w : valid c i} (h' : i = j) : c[i] = c[j]'(h' ▸ w)
theorem getElem_congr_idx [GetElem coll idx elem valid] {c : coll} {i j : idx} {w : valid c i}
(h' : i = j) : c[i] = c[j]'(h' ▸ w) := by
cases h'; rfl
LawfulGetElem
structure
(cont : Type u) (idx : Type v) (elem : outParam (Type w)) (dom : outParam (cont → idx → Prop))
[ge : GetElem? cont idx elem dom]
/--
Lawful `GetElem?` instances (which extend `GetElem`) are those for which the potentially-failing
`GetElem?.getElem?` and `GetElem?.getElem!` operators succeed when the validity predicate is
satisfied, and fail when it is not.
-/
class LawfulGetElem (cont : Type u) (idx : Type v) (elem : outParam (Type w))
(dom : outParam (cont → idx → Prop)) [ge : GetElem? cont idx elem dom] : Prop where
/-- `GetElem?.getElem?` succeeds when the validity predicate is satisfied and fails otherwise. -/
getElem?_def (c : cont) (i : idx) [Decidable (dom c i)] :
c[i]? = if h : dom c i then some (c[i]'h) else none := by
intros
try simp only [getElem?] <;> congr
/-- `GetElem?.getElem!` succeeds and fails when `GetElem.getElem?` succeeds and fails. -/
getElem!_def [Inhabited elem] (c : cont) (i : idx) :
c[i]! = match c[i]? with | some e => e | none => default := by
intros
simp only [getElem!, getElem?, outOfBounds_eq_default]
instLawfulGetElem
instance
[GetElem coll idx elem valid] [∀ xs i, Decidable (valid xs i)] : LawfulGetElem coll idx elem valid
instance (priority := low) [GetElem coll idx elem valid] [∀ xs i, Decidable (valid xs i)] :
LawfulGetElem coll idx elem valid where
getElem?_pos
theorem
[GetElem? cont idx elem dom] [LawfulGetElem cont idx elem dom] (c : cont) (i : idx) (h : dom c i) :
c[i]? = some (c[i]'h)
theorem getElem?_pos [GetElem? cont idx elem dom] [LawfulGetElem cont idx elem dom]
(c : cont) (i : idx) (h : dom c i) : c[i]? = some (c[i]'h) := by
have : Decidable (dom c i) := .isTrue h
rw [getElem?_def]
exact dif_pos h
getElem?_neg
theorem
[GetElem? cont idx elem dom] [LawfulGetElem cont idx elem dom] (c : cont) (i : idx) (h : ¬dom c i) : c[i]? = none
theorem getElem?_neg [GetElem? cont idx elem dom] [LawfulGetElem cont idx elem dom]
(c : cont) (i : idx) (h : ¬dom c i) : c[i]? = none := by
have : Decidable (dom c i) := .isFalse h
rw [getElem?_def]
exact dif_neg h
getElem!_pos
theorem
[GetElem? cont idx elem dom] [LawfulGetElem cont idx elem dom] [Inhabited elem] (c : cont) (i : idx) (h : dom c i) :
c[i]! = c[i]'h
theorem getElem!_pos [GetElem? cont idx elem dom] [LawfulGetElem cont idx elem dom]
[Inhabited elem] (c : cont) (i : idx) (h : dom c i) :
c[i]! = c[i]'h := by
have : Decidable (dom c i) := .isTrue h
simp [getElem!_def, getElem?_def, h]
getElem!_neg
theorem
[GetElem? cont idx elem dom] [LawfulGetElem cont idx elem dom] [Inhabited elem] (c : cont) (i : idx) (h : ¬dom c i) :
c[i]! = default
theorem getElem!_neg [GetElem? cont idx elem dom] [LawfulGetElem cont idx elem dom]
[Inhabited elem] (c : cont) (i : idx) (h : ¬dom c i) : c[i]! = default := by
have : Decidable (dom c i) := .isFalse h
simp [getElem!_def, getElem?_def, h]
get_getElem?
theorem
[GetElem? cont idx elem dom] [LawfulGetElem cont idx elem dom] (c : cont) (i : idx) [Decidable (dom c i)] (h) :
c[i]?.get h = c[i]'(by simp only [getElem?_def] at h; split at h <;> simp_all)
@[simp] theorem get_getElem? [GetElem? cont idx elem dom] [LawfulGetElem cont idx elem dom]
(c : cont) (i : idx) [Decidable (dom c i)] (h) :
c[i]?.get h = c[i]'(by simp only [getElem?_def] at h; split at h <;> simp_all) := by
simp only [getElem?_def] at h ⊢
split <;> simp_all
getElem?_eq_none_iff
theorem
[GetElem? cont idx elem dom] [LawfulGetElem cont idx elem dom] (c : cont) (i : idx) [Decidable (dom c i)] :
c[i]? = none ↔ ¬dom c i
@[simp] theorem getElem?_eq_none_iff [GetElem? cont idx elem dom] [LawfulGetElem cont idx elem dom]
(c : cont) (i : idx) [Decidable (dom c i)] : c[i]?c[i]? = none ↔ ¬dom c i := by
simp only [getElem?_def]
split <;> simp_all
getElem?_eq_none
abbrev
@[deprecated getElem?_eq_none_iff (since := "2025-02-17")]
abbrev getElem?_eq_none := @getElem?_eq_none_iff
isNone_getElem?
abbrev
@[deprecated getElem?_eq_none (since := "2024-12-11")]
abbrev isNone_getElem? := @getElem?_eq_none_iff
isSome_getElem?
theorem
[GetElem? cont idx elem dom] [LawfulGetElem cont idx elem dom] (c : cont) (i : idx) [Decidable (dom c i)] :
c[i]?.isSome = dom c i
@[simp] theorem isSome_getElem? [GetElem? cont idx elem dom] [LawfulGetElem cont idx elem dom]
(c : cont) (i : idx) [Decidable (dom c i)] : c[i]?.isSome = dom c i := by
simp only [getElem?_def]
split <;> simp_all
Fin.instGetElemFinVal
instance
[GetElem cont Nat elem dom] : GetElem cont (Fin n) elem fun xs i => dom xs i
Fin.instGetElem?FinVal
instance
[GetElem? cont Nat elem dom] : GetElem? cont (Fin n) elem fun xs i => dom xs i
Fin.instLawfulGetElemValOfNat
instance
[GetElem? cont Nat elem dom] [h : LawfulGetElem cont Nat elem dom] :
LawfulGetElem cont (Fin n) elem fun xs i => dom xs i
instance [GetElem? cont Nat elem dom] [h : LawfulGetElem cont Nat elem dom] :
LawfulGetElem cont (Fin n) elem fun xs i => dom xs i where
getElem?_def _c _i _d := h.getElem?_def ..
getElem!_def _c _i := h.getElem!_def ..
Fin.getElem_fin
theorem
[GetElem? Cont Nat Elem Dom] (a : Cont) (i : Fin n) (h : Dom a i) : a[i] = a[i.1]
Fin.getElem?_fin
theorem
[h : GetElem? Cont Nat Elem Dom] (a : Cont) (i : Fin n) : a[i]? = a[i.1]?
Fin.getElem!_fin
theorem
[GetElem? Cont Nat Elem Dom] (a : Cont) (i : Fin n) [Inhabited Elem] : a[i]! = a[i.1]!
List.instGetElemNatLtLength
instance
: GetElem (List α) Nat α fun as i => i < as.length
List.getElem_cons_zero
theorem
(a : α) (as : List α) (h : 0 < (a :: as).length) : getElem (a :: as) 0 h = a
List.getElem_cons_succ
theorem
(a : α) (as : List α) (i : Nat) (h : i + 1 < (a :: as).length) :
getElem (a :: as) (i + 1) h = getElem as i (Nat.lt_of_succ_lt_succ h)
@[simp] theorem getElem_cons_succ (a : α) (as : List α) (i : Nat) (h : i + 1 < (a :: as).length) : getElem (a :: as) (i+1) h = getElem as i (Nat.lt_of_succ_lt_succ h) := by
rfl
List.getElem_mem
theorem
: ∀ {l : List α} {n} (h : n < l.length), l[n]'h ∈ l
@[simp] theorem getElem_mem : ∀ {l : List α} {n} (h : n < l.length), l[n]'hl[n]'h ∈ l
| _ :: _, 0, _ => .head ..
| _ :: l, _+1, _ => .tail _ (getElem_mem (l := l) ..)
List.getElem_cons_drop_succ_eq_drop
theorem
{as : List α} {i : Nat} (h : i < as.length) : as[i] :: as.drop (i + 1) = as.drop i
theorem getElem_cons_drop_succ_eq_drop {as : List α} {i : Nat} (h : i < as.length) :
as[i]as[i] :: as.drop (i+1) = as.drop i :=
match as, i with
| _::_, 0 => rfl
| _::_, i+1 => getElem_cons_drop_succ_eq_drop (i := i) (Nat.add_one_lt_add_one_iff.mp h)
List.get_drop_eq_drop
abbrev
@[deprecated getElem_cons_drop_succ_eq_drop (since := "2024-11-05")]
abbrev get_drop_eq_drop := @getElem_cons_drop_succ_eq_drop
List.instGetElem?NatLtLength
instance
: GetElem? (List α) Nat α fun as i => i < as.length
List.get?Internal_eq_getElem?
theorem
{l : List α} {i : Nat} : l.get?Internal i = l[i]?
@[simp] theorem get?Internal_eq_getElem? {l : List α} {i : Nat} :
l.get?Internal i = l[i]? := rfl
List.get!Internal_eq_getElem!
theorem
[Inhabited α] {l : List α} {i : Nat} : l.get!Internal i = l[i]!
List.getElem?_eq_getElem
theorem
{l : List α} {i} (h : i < l.length) : l[i]? = some l[i]
List.getElem?_eq_none_iff
theorem
: l[i]? = none ↔ length l ≤ i
@[simp] theorem getElem?_eq_none_iff : l[i]?l[i]? = none ↔ length l ≤ i :=
match l with
| [] => by simp; rfl
| _ :: l => by
cases i with
| zero => simp
| succ i =>
simp only [length_cons, Nat.add_le_add_iff_right]
exact getElem?_eq_none_iff (l := l) (i := i)
List.none_eq_getElem?_iff
theorem
{l : List α} {i : Nat} : none = l[i]? ↔ length l ≤ i
@[simp] theorem none_eq_getElem?_iff {l : List α} {i : Nat} : nonenone = l[i]? ↔ length l ≤ i := by
simp [eq_comm (a := none)]
List.getElem?_eq_none
theorem
(h : length l ≤ i) : l[i]? = none
theorem getElem?_eq_none (h : length l ≤ i) : l[i]? = none := getElem?_eq_none_iff.mpr h
List.instLawfulGetElemNatLtLength
instance
: LawfulGetElem (List α) Nat α fun as i => i < as.length
Array.instGetElemNatLtSize
instance
: GetElem (Array α) Nat α fun xs i => i < xs.size
Array.instGetElem?NatLtSize
instance
: GetElem? (Array α) Nat α fun xs i => i < xs.size
instance : GetElem? (Array α) Nat α fun xs i => i < xs.size where
getElem? xs i := decidableGetElem? xs i
getElem! xs i := xs.get!Internal i
Array.instLawfulGetElemNatLtSize
instance
: LawfulGetElem (Array α) Nat α fun xs i => i < xs.size
instance : LawfulGetElem (Array α) Nat α fun xs i => i < xs.size where
getElem?_def xs i h := by
simp only [getElem?, decidableGetElem?]
split <;> rfl
getElem!_def xs i := by
simp only [getElem!, getElem?, decidableGetElem?, get!Internal, getD, getElem]
split <;> rfl
Array.getInternal_eq_getElem
theorem
(a : Array α) (i : Nat) (h) : a.getInternal i h = a[i]
@[simp] theorem getInternal_eq_getElem (a : Array α) (i : Nat) (h) :
a.getInternal i h = a[i] := rfl
Array.get!Internal_eq_getElem!
theorem
[Inhabited α] (a : Array α) (i : Nat) : a.get!Internal i = a[i]!
@[simp] theorem get!Internal_eq_getElem! [Inhabited α] (a : Array α) (i : Nat) :
a.get!Internal i = a[i]! := by
simp only [get!Internal, getD, getInternal_eq_getElem, getElem!_def]
split <;> simp_all [getElem?_pos, getElem?_neg]
Lean.Syntax.instGetElemNatTrue
instance
: GetElem Syntax Nat Syntax fun _ _ => True