Just out of personal curiosity because I can't think of any solution. Probably, it's not possible, but I want a decisive answer. Is there some clever way of implementing the following function?
SetAttribute[Generator, HoldFirst]
Generator[code_] := ...
So that it would produce an object that one can iterate over one by one, like this:
gen1 = Generator[Yield[1]; Yield[2]]
gen2 = Generator[For[i = 1, i < 100, i++, Yield[i]]]
gen3 = Generator[i = 1; While[True, Yield[i++]]]
gen1["Next"]
(* 1 *)
gen1["Next"]
(* 2 *)
gen1["Next"]
(* Missing[] *)
Table[gen3["Next"], 10]
(* {1, 2, 3, 4, 5, 6, 7, 8, 9, 10} *)
(* etc. *)
The point is that the generator code can be arbitrary, and it somehow should have access to the global state, save the local one, and give back control to the main loop with the possibility of resuming itself. And ideally, without launching additional kernels. It's not one of the countless lazy implementations people here (including myself) have constructed. Python has them: https://peps.python.org/pep-0255/, JavaScript: https://www.javascripttutorial.net/es6/javascript-yield/ and many more. WL also needs one.
It's like having independent multi-threaded Dialogs running with an ability to Return to any one of them or on-demand session TaskObject that suspends itself (this doesn't work: SessionSubmit[ScheduledTask[For[i = 1, i < 100, i++, TaskSuspend@$CurrentTask], None]], I can't quite figure it out.
What if Yield constructs are dynamically scoped instead of lexically? Probably, lexical scoping is more realistic.