MikeL writes: > > I say this because Damian's coroutine proposal could be greatly > > simplified (IMHO making it clearer and easier) if calling the sub > > didn't imply starting an implicit coroutine the first time. I might > > write something that exemplifies this. > > I'd be quite interested in this -- please do. I *like* Damian's latest > coroutine proposal quite a bit, enough so that it really got me > thinking about how perplexingly lame typical thread syntax is.
Here we go: Damian's current coroutine proposal suggests that to recurse, you start a coroutine of your own code and yield what it yields: coro pre_traverse(%data) { yield %data{value}; yield $_ for <&_.clone(%data{left})>; yield $_ for <&_.clone(%data{right})>; } Now, imagine yourself a beginner and take a long look at this line: yield $_ for <&_.clone(%data{left})>; What the heck? That uses an awful lot of "advanced" Perl 6 features all in one line. And that's the easiest way to recurse? Worse yet, what if you yield I<and> you need to pay attention to return values, which isn't an uncommon thing: coro fibo_tree($n) { return $n if $n <= 1; yield $n; my @ret = <&_.clone($n-1)>; my $n1 = pop @ret; @ret = <&_.clone($n-2)>; my $n2 = pop @ret; return $n1 + $n2; } And all this pain comes from support for this idiom: my $fibo_1 = fibo(); my $fibo_2 = fibo(); Which, as I've described, is "bad form" anyway, because subs ideally keep no state. That's what objects were invented for. C<let>'s remove that ability, hypothetically. Now there's a distinction between calling a coroutine as a coroutine or as a regular sub. When it looks like a sub call, it is a sub call; when it's used as an iterator (perhaps cloned), it's a coroutine. We now have: coro pre_traverse(%data) { yield %data{value}; pre_traverse(%data{left}); pre_traverse(%data{right}); } coro fibo_tree($n) { return $n if $n <= 1; yield $n; return fibo_tree($n-1) + fibo_tree($n-2); } Those are remarkably simpler, and use no "advanced" syntax. To use them, as before: for <&pre_traverse.clone(%tree)> { ... } for <&fibo_tree.clone($n)> { ... } Or better yet: for < coro { pre_traverse(%tree) } > { ... } for < coro { fibo_tree($n) } > { ... } That's not as nice as it could be. I'm not here to rework that part of the syntax, though. It also supports factories for cases like this: sub pre_traverse(%data) { coro (?%arg = %data) { yield %arg{value}; _(%arg{left}); _(%arg{right}); } } for <pre_traverse(%tree)> {...} The overall simplification is that it now supports saving the whole stack again, which results in much clearer and easier recursion. It poses no burden on other kinds of implementations, and removes one bit of the interface that secretly stores state when it shouldn't. Luke