#!/usr/bin/perl
use strict;
use warnings;
use Test::More;
use Test::Fatal;
sub U {
my $f = shift;
sub { $f->($f, @_) };
}
sub Y {
my $f = shift;
U(sub { my $h = shift; sub { $f->(U($h)->())->(@_) } })->();
}
{
package List;
use Moose::Role;
has '_list' => (
is => 'ro',
isa => 'ArrayRef',
init_arg => '::',
default => sub { [] }
);
sub head { (shift)->_list->[0] }
sub tail {
my $self = shift;
(ref $self)->new(
'::' => [
@{$self->_list}[1 .. $#{$self->_list}]
]
);
}
sub print {
join ", " => @{$_[0]->_list};
}
package List::Immutable;
use Moose::Role;
requires 'head';
requires 'tail';
sub is_empty { not defined ($_[0]->head) }
sub length {
my $self = shift;
(::Y(sub {
my $redo = shift;
sub {
my ($list, $acc) = @_;
return $acc if $list->is_empty;
$redo->($list->tail, $acc + 1);
}
}))->($self, 0);
}
sub apply {
my ($self, $function) = @_;
(::Y(sub {
my $redo = shift;
sub {
my ($list, $func, $acc) = @_;
return (ref $list)->new('::' => $acc)
if $list->is_empty;
$redo->(
$list->tail,
$func,
[ @{$acc}, $func->($list->head) ]
);
}
}))->($self, $function, []);
}
package My::List1;
use Moose;
::is( ::exception {
with 'List', 'List::Immutable';
}, undef, '... successfully composed roles together' );
package My::List2;
use Moose;
::is( ::exception {
with 'List::Immutable', 'List';
}, undef, '... successfully composed roles together' );
}
{
my $coll = My::List1->new;
isa_ok($coll, 'My::List1');
ok($coll->does('List'), '... $coll does List');
ok($coll->does('List::Immutable'), '... $coll does List::Immutable');
ok($coll->is_empty, '... we have an empty collection');
is($coll->length, 0, '... we have a length of 1 for the collection');
}
{
my $coll = My::List2->new;
isa_ok($coll, 'My::List2');
ok($coll->does('List'), '... $coll does List');
ok($coll->does('List::Immutable'), '... $coll does List::Immutable');
ok($coll->is_empty, '... we have an empty collection');
is($coll->length, 0, '... we have a length of 1 for the collection');
}
{
my $coll = My::List1->new('::' => [ 1 .. 10 ]);
isa_ok($coll, 'My::List1');
ok($coll->does('List'), '... $coll does List');
ok($coll->does('List::Immutable'), '... $coll does List::Immutable');
ok(!$coll->is_empty, '... we do not have an empty collection');
is($coll->length, 10, '... we have a length of 10 for the collection');
is($coll->print, '1, 2, 3, 4, 5, 6, 7, 8, 9, 10', '... got the right printed value');
my $coll2 = $coll->apply(sub { $_[0] * $_[0] });
isa_ok($coll2, 'My::List1');
is($coll->print, '1, 2, 3, 4, 5, 6, 7, 8, 9, 10', '... original is still the same');
is($coll2->print, '1, 4, 9, 16, 25, 36, 49, 64, 81, 100', '... new collection is changed');
}
{
my $coll = My::List2->new('::' => [ 1 .. 10 ]);
isa_ok($coll, 'My::List2');
ok($coll->does('List'), '... $coll does List');
ok($coll->does('List::Immutable'), '... $coll does List::Immutable');
ok(!$coll->is_empty, '... we do not have an empty collection');
is($coll->length, 10, '... we have a length of 10 for the collection');
is($coll->print, '1, 2, 3, 4, 5, 6, 7, 8, 9, 10', '... got the right printed value');
my $coll2 = $coll->apply(sub { $_[0] * $_[0] });
isa_ok($coll2, 'My::List2');
is($coll->print, '1, 2, 3, 4, 5, 6, 7, 8, 9, 10', '... original is still the same');
is($coll2->print, '1, 4, 9, 16, 25, 36, 49, 64, 81, 100', '... new collection is changed');
}
done_testing;