Perl overload @{} so that you can supply an object to foreach()

The problem that you're having here is not the difference between @{...} and <...> but the difference between foreach and while.

A while loop acts a bit like an iterator in as much as its execution looks like this:

while (you can run a piece of code and get back a value) {
  do something
}

So each time around the loop, it executes the piece of code in the while condition and expects to get a single value back. The code in the while condition is run in scalar context.

A foreach loop, on the other hand, only executes its code once and expects to get a list of values back. The code between parentheses at the start of the loop is executed in list context.

This is why you read a large file a line at a time using:

while (<$file_handle>) {
  ...
}

This only reads a single record from the file at a time. If you used a foreach loop instead like this:

foreach (<$file_handle>) {
  ...
}

then you would get all of the records back from the filehandle at once - which, obviously, takes far more memory.

Dereferencing an array reference works like that too. You get all of the values back at the same time. The overridden method (next()) will only be called once and will be expected to return a list of values.


I show some techniques in Object::Iterate. Give your object some methods to supply the next value and go from there.


[ This adds to Dave Cross's answer rather than being an answer on its own ]

Having a single iterator instance per collection cause all kinds of problem. That's why everyone uses keys instead of each, for example. As such, I strongly recommend that your class produces an iterator rather than being one itself, as the following does:

package My::Collection;

use strict;
use warnings;

use Iterator::Simple qw( iarray );

sub new {
    my ($class) = @_;
    my $self = bless({}, $class);
    $self->{fields} = [];
    return $self;
}

sub add {
    my ($self, $elem) = @_;
    push @{ $self->{fields} }, $elem;
}

sub iter {
    my ($self) = @_;
    return iarray($self->{fields});
}

1;

Use it as follows:

use strict;
use warnings;
use feature qw( say );

use My::Collection qw( );

my $collection = My::Collection->new();
$collection->add("Hi");
$collection->add("Hello");
$collection->add("Yeet");

my $iter = $collection->iter();
while ( my ($item) = $iter->next() ) {
   say $item;
}

You can also use either of the following more magical options:

while ( my ($item) = $iter->() )

while ( my ($item) = <$iter> )