Files
lean4/tests/elab/iterators.lean
Garmelon 08eb78a5b2 chore: switch to new test/bench suite (#12590)
This PR sets up the new integrated test/bench suite. It then migrates
all benchmarks and some related tests to the new suite. There's also
some documentation and some linting.

For now, a lot of the old tests are left alone so this PR doesn't become
even larger than it already is. Eventually, all tests should be migrated
to the new suite though so there isn't a confusing mix of two systems.
2026-02-25 13:51:53 +00:00

393 lines
8.7 KiB
Lean4
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
import Std.Data.Iterators
section ListIteratorBasic
/-- info: [1, 2, 3].iter : Std.Iter Nat -/
#guard_msgs in
#check [1, 2, 3].iter
/-- info: [1, 2, 3].iterM Id : Std.IterM Id Nat -/
#guard_msgs in
#check [1, 2, 3].iterM Id
/-- info: [1, 2, 3] -/
#guard_msgs in
#eval [1, 2, 3].iter.toList
/-- info: [1, 2, 3] -/
#guard_msgs in
#eval [1, 2, 3].iter.ensureTermination.toList
/-- info: [3, 2, 1] -/
#guard_msgs in
#eval [1, 2, 3].iter.toListRev
/-- info: [3, 2, 1] -/
#guard_msgs in
#eval [1, 2, 3].iter.ensureTermination.toListRev
/-- info: #[1, 2, 3] -/
#guard_msgs in
#eval [1, 2, 3].iter.toArray
/-- info: #[1, 2, 3] -/
#guard_msgs in
#eval [1, 2, 3].iter.ensureTermination.toArray
/-- info: ([1, 2, 3].iterM IO).toList : IO (List Nat) -/
#guard_msgs in
#check [1, 2, 3].iterM IO |>.toList
/-- info: [1, 2, 3] -/
#guard_msgs in
#eval [1, 2, 3].iterM IO |>.toList
/-- info: #[1, 2, 3] -/
#guard_msgs in
#eval [1, 2, 3].iterM IO |>.toArray
example : [1, 2, 3].iter.toList = [1, 2, 3] := by
simp
example : [1, 2, 3].iter.toArray = #[1, 2, 3] := by
simp
example : [1, 2, 3].iter.toListRev = [3, 2, 1] := by
simp
-- This does not work for `IO` because `LawfulMonad IO` is in Batteries
example : ([1, 2, 3].iterM (StateM Nat)).toList = pure [1, 2, 3] := by
simp
example : ([1, 2, 3].iterM (StateM Nat)).toArray = pure #[1, 2, 3] := by
simp
example : ([1, 2, 3].iterM (StateM Nat)).toListRev = pure [3, 2, 1] := by
simp
end ListIteratorBasic
section Array
example : #[1, 2, 3].iter.toArray = #[1, 2, 3] := by
simp
example : #[1, 2, 3].iter.toList = [1, 2, 3] := by
simp
example : #[1, 2, 3].iter.toListRev = [3, 2, 1] := by
simp
example : (#[1, 2, 3].iterFromIdx 2).toList = [3] := by
simp
end Array
section WellFoundedRecursion
def sum (l : List Nat) : Nat :=
go l.iter 0
where
go it acc :=
match it.step with
| .yield it' out _ => go it' (acc + out)
| .skip it' _ => go it' acc
| .done _ => acc
termination_by it.finitelyManySteps
/-- info: 6 -/
#guard_msgs in
#eval sum [1, 2, 3]
end WellFoundedRecursion
section Loop
def sumFold (l : List Nat) : Nat :=
l.iter.fold (init := 0) (· + ·)
/-- info: 6 -/
#guard_msgs in
#eval [1, 2, 3].iter.fold (init := 0) (· + · )
example (l : List Nat) : l.iter.fold (init := 0) (· + ·) = l.sum := by
simp [ Std.Iter.foldl_toList, List.sum]
-- It remains to show that List.foldl (· + ·) = List.foldr (· + ·)
generalize 0 = init
induction l generalizing init
· rfl
· rename_i x l ih
simp only [List.foldl_cons, ih, List.foldr_cons]
clear ih
induction l generalizing x
· simp only [List.foldr_nil]
omega
· simp only [List.foldr_cons, *]
omega
example (l : List Nat) :
(Id.run do
let mut s := 0
for x in l.iter do
s := s + x
return s) = l.iter.fold (init := 0) (· + ·) := by
simp
def forInIO (l : List Nat) : IO Nat := do
let mut s := 0
for x in l.iter do
IO.println s!"adding {x}"
s := s + x
return s
/--
info: adding 1
adding 2
---
info: 3
-/
#guard_msgs in
#eval forInIO [1, 2]
end Loop
section Take
def sumTakeRec (l : List Nat) : Nat :=
go (l.iter.take 2) 0
where
go it acc :=
match it.step with
| .yield it' out _ => go it' (acc + out)
| .skip it' _ => go it' acc
| .done _ => acc
termination_by it.finitelyManySteps
def sumTakeFold (l : List Nat) : Nat :=
l.iter.take 2 |>.fold (init := 0) (· + ·)
/-- info: [1, 2] -/
#guard_msgs in
#eval [1, 2, 3].iter.take 2 |>.toList
/-- info: 3 -/
#guard_msgs in
#eval sumTakeRec [1, 2, 3]
/-- info: 3 -/
#guard_msgs in
#eval sumTakeFold [1, 2, 3]
example : ([1, 2, 3].iter.take 2).toList = [1, 2] := by
simp
example : ([1, 2, 3].iter.take 2).toArray = #[1, 2] := by
simp
example : ([1, 2, 3].iter.take 2).toListRev = [2, 1] := by
simp
end Take
section Drop
def sumDropRec (l : List Nat) : Nat :=
go (l.iter.drop 1) 0
where
go it acc :=
match it.step with
| .yield it' out _ => go it' (acc + out)
| .skip it' _ => go it' acc
| .done _ => acc
termination_by it.finitelyManySteps
def sumDropFold (l : List Nat) : Nat :=
l.iter.drop 1 |>.fold (init := 0) (· + ·)
/-- info: [2, 3] -/
#guard_msgs in
#eval [1, 2, 3].iter.drop 1 |>.toList
/-- info: 5 -/
#guard_msgs in
#eval sumDropRec [1, 2, 3]
/-- info: 5 -/
#guard_msgs in
#eval sumDropFold [1, 2, 3]
example : ([1, 2, 3].iter.drop 1).toList = [2, 3] := by
simp
example : ([1, 2, 3].iter.drop 1).toArray = #[2, 3] := by
simp
example : ([1, 2, 3].iter.drop 1).toListRev = [3, 2] := by
simp
end Drop
section FilterMap
example : ([1, 2, 3].iter.filterMap (fun x => if x % 2 = 0 then some (x / 2) else none)).toList =
[1] := by
simp
example : ([1, 2, 3].iter.map (· * 2)).toList = [2, 4, 6] := by
simp
example : ([1, 2, 3].iter.filter (· % 2 = 0)).toList = [2] := by
simp
example : ([1, 2, 3].iter.map (· * 2)).fold (init := 0) (· + ·) = 12 := by
rw [Std.Iter.fold_map, Std.Iter.foldl_toList]
simp
-- This test ensures that the `foldM_mapM` lemma is applicable without producing
-- monad-lifting diamonds. The test is so abstract because using three concrete monads and
-- liftings between them would make the test too complicated.
example {α : Type} {m n o : Type Type} [Monad m] [Monad n] [Monad o]
[LawfulMonad m] [LawfulMonad n] [LawfulMonad o]
[MonadLift m n] [MonadLift n o] [LawfulMonadLift m n] [LawfulMonadLift n o]
[Std.Iterator α m Nat] [Std.Iterators.Finite α m]
[Std.IteratorLoop α m n] [Std.IteratorLoop α m o]
[Std.IteratorLoop α m o]
[Std.LawfulIteratorLoop α m n] [Std.LawfulIteratorLoop α m o]
[Std.LawfulIteratorLoop α m o]
[MonadAttach n] [WeaklyLawfulMonadAttach n]
(it : Std.IterM (α := α) m Nat) (f : Nat n Nat) (g : Nat Nat o Nat) :
(it.mapM f).foldM g init = it.foldM (fun b a => do g b ( f a)) init := by
rw [Std.IterM.foldM_mapM]
/--
info: Lean
is
fun
-/
#guard_msgs in
#eval ["Lean", "is", "fun"].iter.mapM (IO.println s!"{·}") |>.drain
-- This example demonstrates that chained `mapM` calls are executed in a different order than with `List.mapM`.
def chainedMapM (l : List Nat) : IO Unit :=
l.iterM IO |>.mapM (IO.println <| s!"1st {.}") |>.mapM (IO.println <| s!"2nd {·}") |>.drain
/--
info: 1st 1
2nd ()
1st 2
2nd ()
1st 3
2nd ()
-/
#guard_msgs in
#eval! chainedMapM [1, 2, 3]
end FilterMap
section Zip
example : ([1, 2, 3].iter.zip ["one", "two"].iter).toList =
[(1, "one"), (2, "two")] := by
simp
example : ([1, 2, 3].iter.zip ["one", "two"].iter).toListRev =
[(2, "two"), (1, "one")] := by
simp
example : ([1, 2, 3].iter.zip ["one", "two"].iter).toArray =
#[(1, "one"), (2, "two")] := by
simp
end Zip
section TakeWhile
example : ([1, 2, 3, 4].iter.takeWhile (· 3)).toList = [1, 2] := by
simp
example : ([1, 2, 3, 4].iter.takeWhile (· 3)).toArray = #[1, 2] := by
simp
example : ([1, 2, 3, 4].iter.takeWhile (· 3)).toListRev = [2, 1] := by
simp
end TakeWhile
section DropWhile
example : ([1, 2, 3, 4].iter.dropWhile (· 3)).toList = [3, 4] := by
simp
example : ([1, 2, 3, 4].iter.dropWhile (· 3)).toArray = #[3, 4] := by
simp
example : ([1, 2, 3, 4].iter.dropWhile (· 3)).toListRev = [4, 3] := by
simp
end DropWhile
section Repeat
@[simp]
def positives := Std.Iter.repeat (init := 1) (· + 1)
example : (positives.take 5).toList = [1, 2, 3, 4, 5] := by
simp
@[simp]
def divisors (n : Nat) := (positives.take n |>.filter (n % · = 0))
@[simp]
def isPrime (n : Nat) : Bool := (divisors n).toList.length == 2
example : isPrime 5 := by
simp
end Repeat
section Empty
/-- info: [] -/
#guard_msgs in
#eval (Std.Iter.empty Nat).toList
example : (Std.Iter.empty Nat).toList = [] := by
simp
end Empty
section Termination
example := positives.toList
example := positives.toListRev
example := positives.toArray
/--
error: failed to synthesize instance of type class
Std.Iterators.Finite (Std.Iterators.Types.RepeatIterator Nat fun x => x + 1) Id
Hint: Type class instance resolution failures can be inspected with the `set_option trace.Meta.synthInstance true` command.
-/
#guard_msgs in
example := positives.ensureTermination.toList
/--
error: failed to synthesize instance of type class
Std.Iterators.Finite (Std.Iterators.Types.RepeatIterator Nat fun x => x + 1) Id
Hint: Type class instance resolution failures can be inspected with the `set_option trace.Meta.synthInstance true` command.
-/
#guard_msgs in
example := positives.ensureTermination.toListRev
/--
error: failed to synthesize instance of type class
Std.Iterators.Finite (Std.Iterators.Types.RepeatIterator Nat fun x => x + 1) Id
Hint: Type class instance resolution failures can be inspected with the `set_option trace.Meta.synthInstance true` command.
-/
#guard_msgs in
example := positives.ensureTermination.toArray
end Termination