Zulip Chat Archive

Stream: Is there code for X?

Topic: apply function n times


Bernd Losert (Feb 20 2022 at 00:26):

Is there a tactic that can take an n : ℕ, f : α → β, and x : α and produce f (f ... (f x)...), where f appears n times.

Bernd Losert (Feb 20 2022 at 00:26):

(deleted)

Kevin Buzzard (Feb 20 2022 at 00:29):

There's docs#nat.iterate (assuming you mean f : α → α) -- why do you need a tactic?

Bernd Losert (Feb 20 2022 at 00:30):

I can't use iterate because f is polymorphic and each application of f produce different types.

Bernd Losert (Feb 20 2022 at 00:31):

For example, suppose f is functor.map.

Kevin Buzzard (Feb 20 2022 at 00:39):

If the codomain and domain of f are different then I can make no sense of your question. What do the ... mean in your first post?

Yaël Dillies (Feb 20 2022 at 00:41):

How do you know that the n + 1-th application will be polymorphic enough to accept the n-th output?

Bernd Losert (Feb 20 2022 at 00:50):

A more concrete example:

constant xs : list (list (list nat))
constant f : nat  string

def ex : list (list (list string)) := list.map (list.map (list.map f)) xs

Bernd Losert (Feb 20 2022 at 00:51):

I want a tactic where I can write something like (iterate 3 list.map f) xs

Mario Carneiro (Feb 20 2022 at 00:53):

iterate 3 { apply list.map f }, exact xs?

Bernd Losert (Feb 20 2022 at 00:57):

What isiterate in that line of code?

Yaël Dillies (Feb 20 2022 at 00:57):

docs#tactic.interactive.iterate

Bernd Losert (Feb 20 2022 at 01:00):

Ah, OK. I tried

def ex : list (list (list string)) := begin
   iterate 3 { apply list.map f }, exact xs
end

but it complains about apply.

Mario Carneiro (Feb 20 2022 at 01:19):

actually you aren't applying directly to the goal in that example so you have to modify it a bit

def ex : list (list (list string)) := begin
  have := xs, revert this,
  iterate 3 { apply list.map }, exact f
end

Bernd Losert (Feb 20 2022 at 10:51):

Nice. I tried the following, but it does not work:

noncomputable def ex (n : ) : nat.iterate list n string := begin
  have := xs, revert this,
  iterate n { apply list.map }, exact f
end

Reid Barton (Feb 20 2022 at 10:53):

what's the type of xs here?

Bernd Losert (Feb 20 2022 at 10:53):

Oops. I forgot to change that one.

Reid Barton (Feb 20 2022 at 10:53):

the way you're mixing the levels doesn't make sense here, you should just be able to use induction on n though

Bernd Losert (Feb 20 2022 at 10:53):

Let me try again.

Reid Barton (Feb 20 2022 at 10:55):

I feel like the answers involving the iterate tactic were possibly misleading, but then, it's also quite unclear what you want to do or why

Bernd Losert (Feb 20 2022 at 10:56):

Here you go:

def ex (n : ) (f : nat  string) (xs : nat.iterate list n nat) : nat.iterate list n string := begin
  have := xs, revert this,
  iterate n { apply list.map }, exact f
end

Reid Barton (Feb 20 2022 at 10:58):

I guess you should move all arguments (except optionally f) to the right of the colon and use recursion

Bernd Losert (Feb 20 2022 at 11:00):

That's a good idea actually. But why does it not work with the iterate tactic. Does that tactic only work with hard-code n?

Reid Barton (Feb 20 2022 at 11:00):

Right, it just doesn't make sense

Reid Barton (Feb 20 2022 at 11:00):

iterate needs to know how many times to run the body, that number cannot be an internal variable

Bernd Losert (Feb 20 2022 at 11:01):

Bummer... Oh well.

Reid Barton (Feb 20 2022 at 11:01):

There is a phase distinction here

Bernd Losert (Feb 20 2022 at 11:03):

What do you mean by "phase"?

Reid Barton (Feb 20 2022 at 11:04):

e.g. compile-time vs run-time

Reid Barton (Feb 20 2022 at 11:05):

The tactic runs before the function itself (notionally) runs. So n doesn't refer to anything when the tactic executes, it's literally just the letter "n".

Reid Barton (Feb 20 2022 at 11:07):

It's like C macros, you cannot have a program expand a macro n times where n is a number the program read from stdin, because the macro expansion already had to be finished long ago

Reid Barton (Feb 20 2022 at 11:09):

In Lean it would make even less sense because the tactic block could be constructing a proof, and proofs don't even run at all.

Bernd Losert (Feb 20 2022 at 11:10):

Yes, that makes sense. For some reason, I thought I could get away with doing this sort of thing in Lean. I have come across this limitation with other macro systems. I guess this is a fundametal issue with macros in general.

Bernd Losert (Feb 20 2022 at 11:17):

So, I trying this, but I'm getting errors that I don't know how to fix:

def ex (f : nat  string) :  n, (list^[n] nat)  (list^[n] string)
| (0) (n) := f n
| (n + 1) ([]) := []
| (n + 1) (list.cons m ms) := sorry,

Reid Barton (Feb 20 2022 at 11:23):

Hmm, I think nat.iterate is defined inconveniently for this purpose

Reid Barton (Feb 20 2022 at 11:23):

You would prefer to have list^[n+1] nat = list (list^[n] nat)

Bernd Losert (Feb 20 2022 at 11:25):

OK. Let me define my own nat.iterate then and see if that helps.

Reid Barton (Feb 20 2022 at 11:26):

Maybe you can also work with this definition by also generalizing over the types nat and string and the function f, and writing something like | (n + 1) f xs := ex n (list.map f) xs

Reid Barton (Feb 20 2022 at 11:28):

I feel like one of these is less efficient than the other, but I'm not sure which or whether that matters to you.

Bernd Losert (Feb 20 2022 at 11:40):

Progress!

def iter (f : α  α) :  n : , α  α
| 0 := id
| (n + 1) := λ a, f (iter n a)

def ex (f :   string) :  n : , (iter list n nat)  (iter list n string)
| (0) n := f n
| (n + 1) [] := []
| (n + 1) (m :: ms) := ex n m :: list.map (ex n) ms

Reid Barton (Feb 20 2022 at 11:42):

You should be able to use list.map in the (n + 1) case without doing a case split on the list as well.

Bernd Losert (Feb 20 2022 at 11:45):

Good catch. Thanks a lot.

Bernd Losert (Feb 20 2022 at 11:46):

If I have a proof of list^[n+1] nat = list (list^[n] nat), could I use that with the nat.iterate version somehow?

Bernd Losert (Feb 20 2022 at 18:07):

So I tried to generalize the ex example, but it doesn't work:

variables {α β : Type u}
variable {f : Type u  Type u}

def iter (f : α  α) :  n : , α  α
| 0 := id
| (n + 1) := λ a, f (iter n a)

def map [functor f] (m : α  β) :  n, iter f n α  iter f n β
| 0 val := m val
| (n + 1) val := functor.map (map n) val

Reid Barton (Feb 20 2022 at 18:09):

It's probably not obvious to Lean what functor you want to map until you unfold iter

Bernd Losert (Feb 20 2022 at 18:11):

Yes. Should I pass the functor f instance to functor.map?

Bernd Losert (Feb 20 2022 at 18:11):

Yeah, that works.


Last updated: Dec 20 2023 at 11:08 UTC