Zulip Chat Archive
Stream: lean4
Topic: Fin ForIn
Patrick Massot (May 01 2023 at 09:26):
Is there a ForIn
instance on Fin n
somewhere? Or are we meant not to need it?
Patrick Massot (May 01 2023 at 09:28):
Are we meant to rather use docs4#Std.Range.forIn or something like this?
Mario Carneiro (May 01 2023 at 10:13):
It's a bit weird to have an instance directly on numbers, what does it mean to say for x in (2 : Fin 5) do ...
?
Patrick Massot (May 01 2023 at 10:23):
I meant for x in Fin 5 do ...
of course.
Patrick Massot (May 01 2023 at 10:24):
So I guess I meant "a ForIn
instance on Fin
" not "on Fin n
".
Mario Carneiro (May 01 2023 at 10:28):
It is a bit unfortunate that the current version of ForIn
depends on a type rather than a value, since it means you can't have an instance for types directly to support for x in Fin 5 do ...
. One way to work around this is to have a wrapper type
structure AsType (a : A)
but then you still have to write for x in AsType.mk (Fin 5) do ...
. The most ergonomic version of the syntax under the current constraints that I can think of is:
class Elems (α : Type _) (σ : outParam (Type _)) where
elems : σ
open Elems (elems)
structure FinStream (n : Nat) where
pos : Nat
instance : Stream (FinStream n) (Fin n) where
next? | ⟨i⟩ => if h : i < n then some (⟨i, h⟩, ⟨i + 1⟩) else none
instance : Elems (Fin n) (FinStream n) where
elems := ⟨0⟩
#eval show Id (Array (Fin 5)) from do
let mut j := #[]
for i in elems (Fin 5) do
j := j.push i
pure j
-- #[0, 1, 2, 3, 4]
Patrick Massot (May 01 2023 at 10:32):
Thanks. Do you know whether such a solution would compile to something much slows than using Std.range
?
Mario Carneiro (May 01 2023 at 10:33):
It is approximately the same as the Std.Range
implementation, probably faster since it doesn't need a step size, except that this one uses the stream version instead of the direct ForIn impl.
Patrick Massot (May 01 2023 at 10:39):
Ok, nice.
Mario Carneiro (May 01 2023 at 10:40):
Here's an version which uses a direct implementation of ForIn
. It is annoying that there are two ways to make forIn impls...
@[specialize] protected def FinStream.forIn {β : Type u} {m : Type u → Type v} [Monad m]
(f : Fin n → β → m (ForInStep β)) (i : Nat) (b : β) : m β :=
if h : i < n then do
match (← f ⟨i, h⟩ b) with
| ForInStep.done b => pure b
| ForInStep.yield b => FinStream.forIn f (i + 1) b
else
pure b
termination_by _ i _ => n - i
instance : ForIn m (FinStream n) (Fin n) where
forIn i b f := FinStream.forIn f i.1 b
#reduce show Id (Array (Fin 5)) from do
let mut j := #[]
for i in elems (Fin 5) do
j := j.push i
pure j
Mario Carneiro (May 01 2023 at 10:40):
you can tell it is working because the #reduce
at the end doesn't get stuck, unlike the previous example
Mario Carneiro (May 01 2023 at 10:42):
The stream version gets stuck in #reduce
because it is using a partial
function to implement the iteration. Making it work without partial requires std4#127
Eric Wieser (May 01 2023 at 10:46):
Can we hook into docs4#FinEnum here?
Mario Carneiro (May 01 2023 at 10:47):
yeah, the idea is similar to that, except not as antagonistic to producing fast code
Mario Carneiro (May 01 2023 at 10:48):
it wouldn't be hard to layer a LawfulElems
typeclass over top which basically asserts that it produces all the elements, or alternatively it is given a FinEnum
and asserts that it produces the same elements in the same order
Patrick Massot (May 01 2023 at 10:57):
Do you mean such a layer would allow to prove stuff about programs using your ForIn
?
Mario Carneiro (May 01 2023 at 10:58):
well I should hope so
Mario Carneiro (May 01 2023 at 11:00):
any approach that doesn't use partial
(either well founded streams or forIn instances) can in principle be proved to have some behavior, and you probably want to have LawfulElems
if you actually need to know it exhausts the type
Jesse Slater (Oct 18 2023 at 20:08):
This is very nice!
I don't know the guidelines for what gets included in mathlib, but if Fin i -> alpha is the recommended tuple type, should these definitions be added to Mathlib.Data.Fin.Tuple.Reflection?
Scott Morrison (Oct 18 2023 at 22:07):
This sort of stuff is definitely desirable in Mathlib or Std. I think there is quite a bit of design work to do it, however: in the examples above Mario introduces Elems
and FinStream
which would both require API and integration with the library.
Mario Carneiro (Oct 18 2023 at 22:22):
I think it could be better handled by changing the desugaring of for ... in
so that you can pass a type there
Last updated: Dec 20 2023 at 11:08 UTC