The Perl Toolchain Summit needs more sponsors. If your company depends on Perl, please support this very important event.
# --------------------------------------------------------------------------- #
# Seq Role
# --------------------------------------------------------------------------- #
# - An Immutable Sequence role
# --------------------------------------------------------------------------- #

^Seq := ^Role.new({});
^Seq.set_name('Sequence');
^Seq.set_version('0.0.1');
^Seq.set_authority('url:pugscode.org');

# --------------------------------------------------------------------------- #
# Methods dependent upon the repr type, and so need to be defined

^Seq.add_method('head', -> { nil });
^Seq.add_method('tail', -> { nil });

# --------------------------------------------------------------------------- #
# methods not dependent upon the repr type, so we can implement in the role

^Seq.add_method('is_empty', -> { 
	self.head()`is_nil();
});

^Seq.add_method('length', -> { 
	-> $list, $acc {
		&redo := &?SUB;
		$list.is_empty()`if_else(
			-> { $acc },
			-> { &redo`($list.tail(), $acc`increment()) }
		);
	}`(self, 0);
});

^Seq.add_method('reverse', -> { 
	-> $list, @acc {
		&redo := &?SUB;
		$list.is_empty()`if_else(
			-> { $?CLASS.new(@acc) },
			-> { &redo`($list.tail(), [ $list.head() ]`concat(@acc)) }
		);
	}`(self.tail(), [ self.head() ]);
});

^Seq.add_method('apply', -> &code { 
	-> $list, @acc {
	    &redo := &?SUB;
	    $list.is_empty()`if_else(
	        -> { $?CLASS.new(@acc) },
	        -> { &redo`($list.tail(), @acc`push(&func`($list.head()))) }
	    );
	}`(self, []);
});

^Seq.add_method('filter', -> &code {
	-> $list, @acc {
	    &redo := &?SUB;
	    $list.is_empty()`if_else(
	        -> { $?CLASS.new(@acc) },
	        -> {
	            &func`($list.head())`as_bit()`if_else(
	                -> { &redo`($list.tail(), @acc`push($elem)) },
	                -> { &redo`($list.tail(), @acc)             }            
	            );
	        }
	    );
	}`(self, []);
});

^Seq.add_method('reduce', -> &func {
    -> $list, $acc {
        &redo := &?SUB;
        $list.is_empty()`if_else(
            -> { $acc },
            -> { &redo`($list.tail(), &func`($acc, $list.head())) }
        );        
    }`(self.tail(), self.head());
});