This question is in the same ballpark as this other on making blocks iterable, but seems to reveal a different problem with mixins (or a different misunderstanding of the syntax on my part). What Iterable
does is to make a data structure effectively iterable, that is, you can create loops by preceding it with for
.
Iterable serves as an API for objects that can be iterated with the for construct and related iteration constructs, like hyper operators.
So let's try to put this to practice:
my &logger = -> $event {
state %store;
if ( $event ) {
%store{ DateTime.new( now ) } = $event;
} else {
%store;
}
}
role Forable does Iterable {
method iterator(&self:) {
self( Nil );
}
}
logger( "One" );
logger( "Two" );
&logger does Forable;
.say for &logger;
This simply does not work; say
is applied to &logger
as a simple item. However, it works if we change that last sentence to:
.say for &logger.iterator;
Which I guess that indicates that the role is actually working, and mixed in. Since the type for &logger
is Block+{Forable}
, maybe it does not work if Iterable is not mixed in directly. In fact, erasing does Iterable
from the Forable
declaration does not affect it in any way. Let's try then this:
&logger does (Iterable,Forable);
Now the type of &logger
is revealed as Block+{Iterable,Forable}
, but still no joy. iterator
has to be called directly. Any idea on how to solve this?
When does for
call the iterator method?
As I understand, if the (single) argument to an iterating feature is a Scalar
container, then it uses the value in the container and does not call .iterator
. Otherwise, it calls .iterator
on it, evaluating it first if it's an expression or routine call.
&logger does Forable;
.say for &logger;
This simply does not work; say
is applied to &logger
as a simple item.
The &
is a noun marker (sigil) marking a Callable
that is inherently a single thing, a single block of code.
More specifically, &logger
is bound to a Scalar
container whose type is Callable
, exactly the same as $logger
(with a $
sigil) would be if you wrote my Callable $logger
:
say .WHAT, .VAR, .VAR.WHAT, .VAR.of
for my &logger, my Callable $logger
displays:
(Callable)Callable(Scalar)(Callable)
(Callable)Callable(Scalar)(Callable)
Since the type for &logger
is Block+{Forable}
That's actually the type of the Callable
that's contained in the Scalar
container that's bound to &logger
.
It's not the type of the &logger
container itself, which is a Scalar
, as shown above.
When given a single argument in the form of a variable, iterating features like for
look at the variable, not the value contained in the variable, to see if it's Iterable
. A Scalar
is not Iterable
.
Any idea on how to solve this?
See lizmat's answer for one approach.
I don't think you can. The basic problem is that &foo
(the Callable
object) and foo()
(calling the Callable
object) are two very different things.
Feels to me you're trying to add a method
to a class
, but you're working with a Sub
.
You need to mixin the iterator
method on the return value of logger
. As I don't really understand what you're trying to achieve, it's hard to answer the question.
Looking at the result that you apparently want to achieve, I came up with this:
my %store;
multi sub logger() {
%store
}
multi sub logger($event) {
%store{ DateTime.new( now ) } = $event;
}
logger( "One" );
logger( "Two" );
.say for logger;
But that doesn't use role
s at all. So that may not be what you're going for.