Skip to content

Commit 0125ad6

Browse files
authored
feat: Option lemmas (#8379)
This PR adds missing `Option` lemmas. Also: - generalize `bindM` from `Monad` to `Pure` - change the `simp` normal form of both `<|>` and `Option.orElse` to `Option.or`
1 parent 7994e55 commit 0125ad6

File tree

8 files changed

+990
-99
lines changed

8 files changed

+990
-99
lines changed

src/Init/Data/Option/Array.lean

Lines changed: 70 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,33 @@ import Init.Data.Option.List
1111

1212
namespace Option
1313

14+
@[simp, grind] theorem mem_toArray {a : α} {o : Option α} : a ∈ o.toArray ↔ o = some a := by
15+
cases o <;> simp [eq_comm]
16+
17+
@[simp, grind] theorem forIn'_toArray [Monad m] (o : Option α) (b : β) (f : (a : α) → a ∈ o.toArray → β → m (ForInStep β)) :
18+
forIn' o.toArray b f = forIn' o b fun a m b => f a (by simpa using m) b := by
19+
cases o <;> rfl
20+
21+
@[simp, grind] theorem forIn_toArray [Monad m] (o : Option α) (b : β) (f : α → β → m (ForInStep β)) :
22+
forIn o.toArray b f = forIn o b f := by
23+
cases o <;> rfl
24+
25+
@[simp, grind] theorem foldlM_toArray [Monad m] [LawfulMonad m] (o : Option β) (a : α) (f : α → β → m α) :
26+
o.toArray.foldlM f a = o.elim (pure a) (fun b => f a b) := by
27+
cases o <;> simp
28+
29+
@[simp, grind] theorem foldrM_toArray [Monad m] [LawfulMonad m] (o : Option β) (a : α) (f : β → α → m α) :
30+
o.toArray.foldrM f a = o.elim (pure a) (fun b => f b a) := by
31+
cases o <;> simp
32+
33+
@[simp, grind] theorem foldl_toArray (o : Option β) (a : α) (f : α → β → α) :
34+
o.toArray.foldl f a = o.elim a (fun b => f a b) := by
35+
cases o <;> simp
36+
37+
@[simp, grind] theorem foldr_toArray (o : Option β) (a : α) (f : β → α → α) :
38+
o.toArray.foldr f a = o.elim a (fun b => f b a) := by
39+
cases o <;> simp
40+
1441
@[simp]
1542
theorem toList_toArray {o : Option α} : o.toArray.toList = o.toList := by
1643
cases o <;> simp
@@ -23,4 +50,47 @@ theorem toArray_filter {o : Option α} {p : α → Bool} :
2350
(o.filter p).toArray = o.toArray.filter p := by
2451
rw [← toArray_toList, toList_filter, ← List.filter_toArray, toArray_toList]
2552

53+
theorem toArray_bind {o : Option α} {f : α → Option β} :
54+
(o.bind f).toArray = o.toArray.flatMap (Option.toArray ∘ f) := by
55+
cases o <;> simp
56+
57+
theorem toArray_join {o : Option (Option α)} : o.join.toArray = o.toArray.flatMap Option.toArray := by
58+
simp [toArray_bind, ← bind_id_eq_join]
59+
60+
theorem toArray_map {o : Option α} {f : α → β} : (o.map f).toArray = o.toArray.map f := by
61+
cases o <;> simp
62+
63+
theorem toArray_min [Min α] {o o' : Option α} :
64+
(min o o').toArray = o.toArray.zipWith min o'.toArray := by
65+
cases o <;> cases o' <;> simp
66+
67+
@[simp]
68+
theorem size_toArray_le {o : Option α} : o.toArray.size ≤ 1 := by
69+
cases o <;> simp
70+
71+
theorem size_toArray_eq_ite {o : Option α} :
72+
o.toArray.size = if o.isSome then 1 else 0 := by
73+
cases o <;> simp
74+
75+
@[simp]
76+
theorem toArray_eq_empty_iff {o : Option α} : o.toArray = #[] ↔ o = none := by
77+
cases o <;> simp
78+
79+
@[simp]
80+
theorem toArray_eq_singleton_iff {o : Option α} : o.toArray = #[a] ↔ o = some a := by
81+
cases o <;> simp
82+
83+
@[simp]
84+
theorem size_toArray_eq_zero_iff {o : Option α} :
85+
o.toArray.size = 0 ↔ o = none := by
86+
cases o <;> simp
87+
88+
@[simp]
89+
theorem size_toArray_eq_one_iff {o : Option α} :
90+
o.toArray.size = 1 ↔ o.isSome := by
91+
cases o <;> simp
92+
93+
theorem size_toArray_choice_eq_one [Nonempty α] : (choice α).toArray.size = 1 := by
94+
simp
95+
2696
end Option

src/Init/Data/Option/Attach.lean

Lines changed: 169 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -8,11 +8,16 @@ module
88
prelude
99
import Init.Data.Option.Basic
1010
import Init.Data.Option.List
11+
import Init.Data.Option.Array
12+
import Init.Data.Array.Attach
1113
import Init.Data.List.Attach
1214
import Init.BinderPredicates
1315

1416
namespace Option
1517

18+
instance {o : Option α} : Subsingleton { x // o = some x } where
19+
allEq a b := Subtype.ext (Option.some.inj (a.property.symm.trans b.property))
20+
1621
/--
1722
Unsafe implementation of `attachWith`, taking advantage of the fact that the representation of
1823
`Option {x // P x}` is the same as the input `Option α`.
@@ -86,7 +91,7 @@ theorem attachWith_map_subtype_val {p : α → Prop} (o : Option α) (H : ∀ a,
8691
(o.attachWith p H).map Subtype.val = o :=
8792
(attachWith_map_val _ _ _).trans (congrFun Option.map_id _)
8893

89-
theorem attach_eq_some : ∀ (o : Option a) (x : {x // o = some x}), o.attach = some x
94+
theorem attach_eq_some : ∀ (o : Option α) (x : {x // o = some x}), o.attach = some x
9095
| none, ⟨x, h⟩ => by simp at h
9196
| some a, ⟨x, h⟩ => by simpa using h
9297

@@ -123,20 +128,41 @@ theorem mem_attach : ∀ (o : Option α) (x : {x // o = some x}), x ∈ o.attach
123128
cases o <;> cases x <;> simp
124129

125130
@[simp] theorem get_attach {o : Option α} (h : o.attach.isSome = true) :
126-
o.attach.get h = ⟨o.get (by simpa using h), by simp⟩ := by
127-
cases o
128-
· simp at h
129-
· simp [get_some]
131+
o.attach.get h = ⟨o.get (by simpa using h), by simp⟩ :=
132+
Subsingleton.elim _ _
133+
134+
@[simp] theorem getD_attach {o : Option α} {fallback} :
135+
o.attach.getD fallback = fallback :=
136+
Subsingleton.elim _ _
137+
138+
@[simp] theorem get!_attach {o : Option α} [Inhabited { x // o = some x }] :
139+
o.attach.get! = default :=
140+
Subsingleton.elim _ _
130141

131142
@[simp] theorem get_attachWith {p : α → Prop} {o : Option α} (H : ∀ a, o = some a → p a) (h : (o.attachWith p H).isSome) :
132143
(o.attachWith p H).get h = ⟨o.get (by simpa using h), H _ (by simp)⟩ := by
133-
cases o
134-
· simp at h
135-
· simp [get_some]
144+
cases o <;> simp
145+
146+
@[simp] theorem getD_attachWith {p : α → Prop} {o : Option α} {h} {fallback} :
147+
(o.attachWith p h).getD fallback =
148+
⟨o.getD fallback.1, by cases o <;> (try exact fallback.2) <;> exact h _ (by simp)⟩ := by
149+
cases o <;> simp
136150

137151
theorem toList_attach (o : Option α) :
138-
o.attach.toList = o.toList.attach.map fun ⟨x, h⟩ => ⟨x, by simpa using h⟩ := by
139-
cases o <;> simp [toList]
152+
o.attach.toList = o.toList.attach.map fun x => ⟨x.1, by simpa using x.2⟩ := by
153+
cases o <;> simp
154+
155+
theorem toList_attachWith {p : α → Prop} {o : Option α} {h} :
156+
(o.attachWith p h).toList = o.toList.attach.map fun x => ⟨x.1, h _ (by simpa using x.2)⟩ := by
157+
cases o <;> simp
158+
159+
theorem toArray_attach (o : Option α) :
160+
o.attach.toArray = o.toArray.attach.map fun x => ⟨x.1, by simpa using x.2⟩ := by
161+
cases o <;> simp
162+
163+
theorem toArray_attachWith {p : α → Prop} {o : Option α} {h} :
164+
(o.attachWith p h).toArray = o.toArray.attach.map fun x => ⟨x.1, h _ (by simpa using x.2)⟩ := by
165+
cases o <;> simp
140166

141167
@[simp, grind =] theorem attach_toList (o : Option α) :
142168
o.toList.attach = (o.attach.map fun ⟨a, h⟩ => ⟨a, by simpa using h⟩).toList := by
@@ -203,6 +229,11 @@ theorem attach_filter {o : Option α} {p : α → Bool} :
203229
· rintro ⟨h, rfl⟩
204230
simp [h]
205231

232+
theorem filter_attachWith {P : α → Prop} {o : Option α} {h : ∀ x, o = some x → P x} {q : α → Bool} :
233+
(o.attachWith P h).filter q =
234+
(o.filter q).attachWith P (fun _ h' => h _ (eq_some_of_filter_eq_some h')) := by
235+
cases o <;> simp [filter_some] <;> split <;> simp
236+
206237
theorem filter_attach {o : Option α} {p : {x // o = some x} → Bool} :
207238
o.attach.filter p = o.pbind fun a h => if p ⟨a, h⟩ then some ⟨a, h⟩ else none := by
208239
cases o <;> simp [filter_some]
@@ -211,6 +242,64 @@ theorem toList_pbind {o : Option α} {f : (a : α) → o = some a → Option β}
211242
(o.pbind f).toList = o.attach.toList.flatMap (fun ⟨x, h⟩ => (f x h).toList) := by
212243
cases o <;> simp
213244

245+
theorem toArray_pbind {o : Option α} {f : (a : α) → o = some a → Option β} :
246+
(o.pbind f).toArray = o.attach.toArray.flatMap (fun ⟨x, h⟩ => (f x h).toArray) := by
247+
cases o <;> simp
248+
249+
theorem toList_pfilter {o : Option α} {p : (a : α) → o = some a → Bool} :
250+
(o.pfilter p).toList = (o.toList.attach.filter (fun x => p x.1 (by simpa using x.2))).unattach := by
251+
cases o with
252+
| none => simp
253+
| some a =>
254+
simp only [pfilter_some, toList_some, List.attach_cons, List.attach_nil, List.map_nil]
255+
split <;> rename_i h
256+
· rw [List.filter_cons_of_pos h]; simp
257+
· rw [List.filter_cons_of_neg h]; simp
258+
259+
theorem toArray_pfilter {o : Option α} {p : (a : α) → o = some a → Bool} :
260+
(o.pfilter p).toArray = (o.toArray.attach.filter (fun x => p x.1 (by simpa using x.2))).unattach := by
261+
cases o with
262+
| none => simp
263+
| some a =>
264+
simp only [pfilter_some, toArray_some, List.attach_toArray, List.attachWith_mem_toArray,
265+
List.attach_cons, List.attach_nil, List.map_nil, List.map_cons, List.size_toArray,
266+
List.length_cons, List.length_nil, Nat.zero_add, List.filter_toArray', List.unattach_toArray]
267+
split <;> rename_i h
268+
· rw [List.filter_cons_of_pos h]; simp
269+
· rw [List.filter_cons_of_neg h]; simp
270+
271+
theorem toList_pmap {p : α → Prop} {o : Option α} {f : (a : α) → p a → β}
272+
(h : ∀ a, o = some a → p a) :
273+
(o.pmap f h).toList = o.attach.toList.map (fun x => f x.1 (h _ x.2)) := by
274+
cases o <;> simp
275+
276+
theorem toArray_pmap {p : α → Prop} {o : Option α} {f : (a : α) → p a → β}
277+
(h : ∀ a, o = some a → p a) :
278+
(o.pmap f h).toArray = o.attach.toArray.map (fun x => f x.1 (h _ x.2)) := by
279+
cases o <;> simp
280+
281+
theorem attach_pfilter {o : Option α} {p : (a : α) → o = some a → Bool} :
282+
(o.pfilter p).attach =
283+
o.attach.pbind fun x h => if h' : p x (by simp_all) then
284+
some ⟨x.1, by simpa [pfilter_eq_some_iff] using ⟨_, h'⟩⟩ else none := by
285+
cases o with
286+
| none => simp
287+
| some a =>
288+
simp only [attach_some, eq_mp_eq_cast, id_eq, pbind_some]
289+
rw [attach_congr pfilter_some]
290+
split <;> simp [*]
291+
292+
theorem attach_guard {p : α → Bool} {x : α} :
293+
(guard p x).attach = if h : p x then some ⟨x, by simp_all⟩ else none := by
294+
simp only [guard_eq_ite]
295+
split <;> simp [*]
296+
297+
theorem attachWith_guard {q : α → Bool} {x : α} {P : α → Prop}
298+
{h : ∀ a, guard q x = some a → P a} :
299+
(guard q x).attachWith P h = if h' : q x then some ⟨x, h _ (by simp_all)⟩ else none := by
300+
simp only [guard_eq_ite]
301+
split <;> simp [*]
302+
214303
/-! ## unattach
215304
216305
`Option.unattach` is the (one-sided) inverse of `Option.attach`. It is a synonym for `Option.map Subtype.val`.
@@ -255,6 +344,29 @@ def unattach {α : Type _} {p : α → Prop} (o : Option { x // p x }) := o.map
255344
(o.attachWith p H).unattach = o := by
256345
cases o <;> simp
257346

347+
theorem unattach_eq_some_iff {p : α → Prop} {o : Option { x // p x }} {x : α} :
348+
o.unattach = some x ↔ ∃ h, o = some ⟨x, h⟩ :=
349+
match o with
350+
| none => by simp
351+
| some ⟨y, h⟩ => by simpa using fun h' => h' ▸ h
352+
353+
@[simp]
354+
theorem unattach_eq_none_iff {p : α → Prop} {o : Option { x // p x }} :
355+
o.unattach = none ↔ o = none := by
356+
cases o <;> simp
357+
358+
theorem get_unattach {p : α → Prop} {o : Option { x // p x }} {h} :
359+
o.unattach.get h = (o.get (by simpa using h)).1 := by
360+
cases o <;> simp
361+
362+
theorem toList_unattach {p : α → Prop} {o : Option { x // p x }} :
363+
o.unattach.toList = o.toList.unattach := by
364+
cases o <;> simp
365+
366+
theorem toArray_unattach {p : α → Prop} {o : Option { x // p x }} :
367+
o.unattach.toArray = o.toArray.unattach := by
368+
cases o <;> simp
369+
258370
/-! ### Recognizing higher order functions on subtypes using a function that only depends on the value. -/
259371

260372
/--
@@ -279,4 +391,51 @@ and simplifies these to the function directly taking the value.
279391
· simp only [filter_some, hf, unattach_some]
280392
split <;> simp
281393

394+
@[simp] theorem unattach_guard {p : α → Prop} {q : { x // p x } → Bool} {r : α → Bool}
395+
(hq : ∀ x h, q ⟨x, h⟩ = r x) {x : { x // p x }} :
396+
(guard q x).unattach = guard r x.1 := by
397+
simp only [guard]
398+
split <;> simp_all
399+
400+
@[simp] theorem unattach_pfilter {p : α → Prop} {o : Option { x // p x }}
401+
{f : (a : { x // p x }) → o = some a → Bool}
402+
{g : (a : α) → o.unattach = some a → Bool} (hf : ∀ x h h', f ⟨x, h⟩ h' = g x (by simp_all)) :
403+
(o.pfilter f).unattach = o.unattach.pfilter g := by
404+
cases o with
405+
| none => simp
406+
| some a =>
407+
simp only [hf, pfilter_some, unattach_some]
408+
split <;> simp
409+
410+
@[simp] theorem unattach_merge {p : α → Prop} {f : { x // p x } → { x // p x } → { x // p x }}
411+
{g : α → α → α} (hf : ∀ x h y h', (f ⟨x, h⟩ ⟨y, h'⟩).1 = g x y) {o o' : Option { x // p x }} :
412+
(o.merge f o').unattach = o.unattach.merge g o'.unattach := by
413+
cases o <;> cases o' <;> simp [*]
414+
415+
theorem any_attach {p : α → Bool} {o : Option α} {q : { x // o = some x } → Bool}
416+
(h : ∀ x h, q ⟨x, h⟩ = p x) : o.attach.any q = o.any p := by
417+
cases o <;> simp [*]
418+
419+
theorem any_attachWith {p : α → Bool} {o : Option α} {r : α → Prop} (hr : ∀ x, o = some x → r x)
420+
{q : { x // r x } → Bool}
421+
(h : ∀ x h, q ⟨x, h⟩ = p x) : (o.attachWith r hr).any q = o.any p := by
422+
cases o <;> simp [*]
423+
424+
theorem any_unattach {p : α → Prop} {o : Option { x // p x }} {q : α → Bool} :
425+
o.unattach.any q = o.any (q ∘ Subtype.val) := by
426+
cases o <;> simp
427+
428+
theorem all_attach {p : α → Bool} {o : Option α} {q : { x // o = some x } → Bool}
429+
(h : ∀ x h, q ⟨x, h⟩ = p x) : o.attach.all q = o.all p := by
430+
cases o <;> simp [*]
431+
432+
theorem all_attachWith {p : α → Bool} {o : Option α} {r : α → Prop} (hr : ∀ x, o = some x → r x)
433+
{q : { x // r x } → Bool}
434+
(h : ∀ x h, q ⟨x, h⟩ = p x) : (o.attachWith r hr).all q = o.all p := by
435+
cases o <;> simp [*]
436+
437+
theorem all_unattach {p : α → Prop} {o : Option { x // p x }} {q : α → Bool} :
438+
o.unattach.all q = o.all (q ∘ Subtype.val) := by
439+
cases o <;> simp
440+
282441
end Option

src/Init/Data/Option/Basic.lean

Lines changed: 3 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -102,11 +102,9 @@ From the perspective of `Option` as a collection with at most one element, the m
102102
is applied to the element if present, and the final result is empty if either the initial or the
103103
resulting collections are empty.
104104
-/
105-
@[inline] protected def bindM [Monad m] (f : α → m (Option β)) (o : Option α) : m (Option β) := do
106-
if let some a := o then
107-
return (← f a)
108-
else
109-
return none
105+
@[inline] protected def bindM [Pure m] (f : α → m (Option β)) : Option α → m (Option β)
106+
| none => pure none
107+
| some a => f a
110108

111109
/--
112110
Applies a function in some applicative functor to an optional value, returning `none` with no

0 commit comments

Comments
 (0)