Quick actions

cmd+k|ctrl+k

Navigation

Languages

terse Raku `transduce`

Snippet info

Language

Raku

Visibility

public

Author

raiph

Created

2026-02-22T00:08:39.845223Z

Updated

2026-02-22T00:16:52.576434Z

sub transduce ($stream, |transducers ) {

    my ($stream-iterator, $input);

    # Step through specified transducers:
    gather transducer: for transducers -> $transducer {
        
        # Start pulling from $stream.
        # Code written here inside the `for transducers` iteration for right scoping of the `last` in &pull-one.
        # Next line is called once when `transduce` sub execution begins, BEFORE `for transducers` loop begins.
        FIRST { $stream-iterator = $stream .iterator; $input = pull-one }
        # `last` out of the `for transducers` iteration above if stream's IterationEnd reached:
        sub pull-one { my $element := $stream-iterator .pull-one; last if $element =:= IterationEnd; $element }

        my role Keep {}
        # Extract or construct transducer (consumer => producer pair) given each transducer's rule type.
        # This transducer `given`/`when` must be in sync with the consumer & producer ones further down.
        my ($consumer, $producer) = do given $transducer { # eg transducer rule: does this transduction:   
            when Array[Code] { .[0],    Keep      }        # `[* %% 2]`          Keeps while number is even
            when []          { Keep,    Keep      }        # `[]`                Keeps entire rest of stream
            when Pair        { .key[0], .value[0] }        # `[] => Nil`         Skips entire rest of stream
                                                           # `10 => Nil`         Skips 10 elements of stream
                                                           # `[*.Int] => [++*]`  Adds 1 to input while Int
            default          { .[0],    Keep      }        # `* %% 2`            Keeps until number not even
                                                           # `10`                Keeps 10 elements of stream
        }

        # Step through stream:
        loop {

            # All consumers except Keep (`[]`) have a test. If test fails, time for next transducer:             
            given $consumer {
                when Code   { next transducer unless .($input)   }
                when Int    { next transducer unless $consumer-- }
            }

            # A Keep producer always outputs (takes) the input (aka keep).
            # A `Nil` producer always outputs nothing (aka skip).
            # A code producer that returns an undefined value when run outputs nothing (aka conditional skip).
            # A code producer that returns a defined value outputs that value.
            # Any other producer value outputs the producer as the value.
            given $producer {
                when Keep  { take $input                          } # If no explicit producer (or `[]` CONSUMER) then Keep
                when Any:U {                                      } # `=> Nil` producer means take nothing (aka skip it)
                when Code  { .($input) andthen .take if *.defined } # $producer($input) is taken unless result is Empty
                default    { .take                                } # If none of the above, take $producer as is as value
            }
            
            $input = pull-one
        }
    }
}

say transduce ^20, 3 => 'strings', [* < 6] => Nil, [* < 10] => [++*], [* %% 5] => 'divisible by 5', [] => -1;
INFO