
use v6-alpha;

role Array--Perl6;

does Map;
does List;

# push @foo, self



&Array::pop:(Array) := &Array::splice:(Array).assuming(:offset(-1) :length(1));

multi sub Array::pop () returns Scalar {
    pop @CALLER::_;
}

 multi sub Array::push (@array is rw ; *@values) returns Int {
   Array::splice(@array, @array.elems, 0, @values);
   @array.elems;
 }
 &Array::shift:(Array) := &Array::splice:(Array).assuming(:offset(0) :length(1));

 multi sub Array::shift () returns Scalar {
   Array::shift @CALLER::_;
 }
 multi sub Array::splice (       @array is rw 
                                 : Int ?$offset = 0,
                                   Int ?$length,
                                       *@values
                                ) returns List is rw
 multi sub Array::unshift (@array is rw ; *@values) returns Int {
   Array::splice(@array, 0, 0, @values);
   @array.elems;
 }

 multi sub Array::grep (@values ;      Code *&test  ) returns Lazy
 multi sub Array::grep (@values,   MatchTest $test  ) returns Lazy
 multi sub  Perl6::List::grep (MatchTest $test ;   *@values) returns Lazy {
   gather {
     for @values -> $x {
       take $x if $x ~~ $test;
     }
   }
 }
 multi sub Array::join (@values,   Str $delimiter) returns Str
 multi sub  Perl6::List::join (Str $delimiter ; *@values) returns Str {
   my $str = ~@values[0];
   for 1..@values.end {
     $str ~= $delimiter ~ @values[$_];
   }
   $str;
 }
 &join := &join:(Str).assuming:delimiter(' ');

 multi sub Array::map (@values,   Code $expression) returns Lazy 
 multi sub  Perl6::List::map (Code $expression ; *@values) returns Lazy {
   gather {
     while @values {
       take $expression
          .( splice(@values, 0, $expression.arity) );
     }
   }
 }
 multi sub Array::reduce (@values ; Code *&expression) returns Scalar
 multi sub  Perl6::List::reduce (Code $expression ; *@values) returns Scalar {
   my $res;
   for @values -> $cur {
     FIRST {$res = $cur; next;}
     $res = &$expression($res, $cur);
   }
   $res;
 }


 multi sub Perl6::Hash::reverse (%hash) returns Hash is default {
   my %result;
   for %hash.kv -> $k, $v {
     %result{$v} = $k;
   }
   %result;
 }

 multi sub Array::reverse (   @values) returns Lazy|Str {
 multi sub  Perl6::List::reverse (; *@values) returns Lazy|Str {
   given want {
     when List {
       gather {
         1 while take pop @values;
       }
     }
     when Scalar {
       reverse @values ==> join;
     }
   }
 }
 type KeyExtractor  ::= Code(Any) returns Any;
 type Comparator    ::= Code(Any, Any) returns Int;
 type SortCriterion ::= KeyExtractor
                      | Comparator
                      | Pair(KeyExtractor, Comparator);

 multi sub Array::sort(                 @values is rw,
                                              *&by
                              ;           Bit :$inplace
                             ) returns Array

 multi sub Array::sort(                 @values is rw,
                                SortCriterion  @by
                              ;           Bit :$inplace
                             ) returns Array

 multi sub Array::sort(                 @values is rw
                              ; SortCriterion :$by = &infix:<cmp>,
                                          Bit :$inplace
                             ) returns Array

 multi sub  Perl6::List::sort(  SortCriterion  @by
                              ;               *@values
                             ) returns List

 multi sub  Perl6::List::sort(; SortCriterion  $by = &infix:<cmp>,
                                              *@values
                             ) returns List
 multi sub Perl6::Lists::zip (; Array *@lists, Bit :$shortest) returns Lazy {
   gather {
     while $shortest ?? all (@lists) !! any(@lists) {
       for @lists -> @list {
         take shift @list;
       }
     }
   }
 }

