Zulip Chat Archive
Stream: new members
Topic: Asserting that `x ∈ ls` for `x` in `ls.map fun x =>`
Ayhon (Sep 07 2024 at 20:46):
I've found myself in the need to prove that, inside a function passed to List.map
, the elements passed to the function belong to the list. This could be achieved using the auxiliary lemma List.exists_of_mem_map: ∀ {α : Type u_1} {α_1 : Type u_2} {f : α → α_1} {l : List α} {b : α_1}, b ∈ List.map f l → ∃ a ∈ l, f a = b
, except that I need this fact to be true and accessible from whithin the function itself.
For a simplified example, consider the implementation of the following function.
import Batteries.Data.Vector.Basic
open Batteries
variable{size: Nat}{α : Type}
def collectEven(v: Vector α (2*size)): Vector α size :=
let indices := List.range size
let data := indices.map fun i =>
v[2*i] -- map_function
Vector.mk ⟨data⟩ $ by sorry
Ignoring the sorry at the end, Lean4 refused to accept the current execution since it's unable to ascertain that the elements in indices
are in fact less than size
. We can obtain ∀ i, i ∈ List.range size -> i < size
from List.mem_range
, but I'm unsure of how to obtain that i ∈ indices
from within the function marked as map_function
in the code.
Am I approaching this the wrong way? Is there an easier way to solve this problem?
Kyle Miller (Sep 07 2024 at 20:54):
docs#List.attach is useful here
def collectEven (v: Vector α (2*size)): Vector α size :=
let indices := List.range size
let data := indices.attach.map fun i =>
v[2*i.1]'(by have := i.2; simp [indices] at this; omega)
Vector.mk ⟨data⟩ $ by simp [data, indices]
Ayhon (Sep 07 2024 at 20:55):
This is it! Thanks a lot!
Ayhon (Sep 07 2024 at 20:56):
Out of curiosity, how could I have found this method on my own? I tried using Loogle or simply looking at the List
implementation, but couldn't find anything relevant. Is this simply due to inexperience?
Kyle Miller (Sep 07 2024 at 21:00):
I learned about .attach
at some point by seeing it used, maybe here on Zulip.
Kyle Miller (Sep 07 2024 at 21:01):
I first saw docs#Finset.attach and figured List
would have it too.
Eric Wieser (Sep 07 2024 at 21:11):
Was having x ∈ ls
available as an argument to ls.map
ever considered? If so, it might make sense for the docstring of List.map
to explain why that turned out to be a bad idea, and to point to List.attach
and List.map_congr
.
Ayhon (Sep 07 2024 at 21:13):
Eric Wieser said:
Was having
x ∈ ls
available as an argument tols.map
ever considered? If so, it might make sense for the docstring ofList.map
to explain why that turned out to be a bad idea, and to point toList.attach
andList.map_congr
.
I think that's a good idea. I know I would have appreciated it. Should I open an issue / PR in GitHub?
Kyle Miller (Sep 07 2024 at 21:14):
That makes map
much more dependent, and you can't rewrite ls
. The zen of attach
is that ls.attach.map
is how you get this dependent version — you can opt-in to the additional difficulty.
It would be great if List.map
mentioned List.map_congr
and List.attach
Eric Wieser (Sep 07 2024 at 21:40):
You can rewrite ls
in the non-dependent case, right?
def List.map' {α β} (l : List α) (f : (a : α) → a ∈ l → β) : List β :=
match l with
| [] => []
| x :: xs => f x (mem_cons_self _ _) :: xs.map' fun a ha => f a <| mem_cons_of_mem _ ha
example (l₁ l₂ : List Nat) (h : l₁ ++ l₂ = l₂ ++ l₁) :
(l₁ ++ l₂).map' (fun a _ => a*a) = (l₂ ++ l₁).map' (fun a _ => a*a) := by
rw [h]
Eric Wieser (Sep 07 2024 at 21:41):
That is, it's not clear that making map
dependent incurs any costs for non-dependent uses, besides downstream churn and an extra underscore.
Eric Wieser (Sep 07 2024 at 21:56):
It's possible that the zen ofl.attach.map fun a => f a.val a.prop
instead of l.map' fun a ha => f a ha
is a historical artifact caused by the fact that map
lived in core and was effectively immutable, while docs3#list.attach lived in mathlib
Daniel Weber (Sep 08 2024 at 01:13):
Eric Wieser said:
Was having
x ∈ ls
available as an argument tols.map
ever considered? If so, it might make sense for the docstring ofList.map
to explain why that turned out to be a bad idea, and to point toList.attach
andList.map_congr
.
Isn't that docs#List.pmap ?
Last updated: May 02 2025 at 03:31 UTC