Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When does for call the iterator method?

Tags:

mixins

raku

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?

like image 626
jjmerelo Avatar asked May 29 '18 09:05

jjmerelo


2 Answers

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 roles at all. So that may not be what you're going for.

like image 82
Elizabeth Mattijsen Avatar answered Nov 15 '22 08:11

Elizabeth Mattijsen


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.

like image 25
raiph Avatar answered Nov 15 '22 08:11

raiph