The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
# --------------------------------------------------------------------------- #
# LazySequence Class
# --------------------------------------------------------------------------- #
# - An Lazy Immutable Sequence class
# --------------------------------------------------------------------------- #

^LazySequence := ^Class.new({});
^LazySequence.set_name('LazySequence');
^LazySequence.set_version('0.0.1');
^LazySequence.set_authority('url:pugscode.org');

^LazySequence.set_roles([ ^Seq ]);

^LazySequence.set_superclasses([ ^Object ]);

^LazySequence.add_attribute('$!current',   nil); 
^LazySequence.add_attribute('&generator', nil); 

^LazySequence.add_method('BUILD', -> %params {  
    # cache the current (head) element 
    # at instance creation time :)
	self`set_attr('$!current', self`get_attr('&generator')`());
});

# --------------------------------------------------------------------------- #
# Methods dependent upon the repr type

^LazySequence.add_method('head', -> {  self`get_attr('$!current') });
^LazySequence.add_method('tail', -> { 
    # create a new instance, which then forces the 
    # current element to be generated (see BUILD above)
    $?CLASS.new({ '&generator' => self`get_attr('&generator') });
});

# these two need to be re-implemented to return LazySequences

^LazySequence.add_method('apply', -> &code {
	&old_generator := self`get_attr('&generator');
	$?CLASS.new({ '&generator' => -> { &code`(&old_generator`()) } });	
});

^LazySequence.add_method('filter', -> &code {
    &old_generator := self`get_attr('&generator');
    $?CLASS.new({ '&generator' => -> { 
            &redo := &?SUB;
            my $value = &old_generator`();
            &code`($value)`as_bit()`if_else(
                -> { $value   },
                -> { &redo`() }
            ); 
        } 
    });
});

# NOTE:
# ideally this would not need to be re-implemented
# here just so that I could return a non-lazy Sequence
# but I see no other way currently.
^LazySequence.add_method('reverse', -> { 
	-> $list, @acc {
		&redo := &?SUB;
		$list.is_empty()`if_else(
			-> { ^Sequence.new(@acc) }, # << Must return a regular Sequcene type
			-> { &redo`($list.tail(), [ $list.head() ]`concat(@acc)) }
		);
	}`(self.tail(), [ self.head() ]);
});