In APL there is the power operator ⍣
, which if applied to a function f
superimposes the application of f
. How to implement that operator in Raku?
For example, with this definition of f
:
sub f(Int:D $i){ $i + 1 }
the command say (f ⍣ 4)(10);
should be equivalent to say f(f(f(f(10))));
.
My implementation below is for a function with one argument.
Questions
How one should extend or replace it with a better implementation that works on multiple (or any) signatures?
How to define "high precedence" of that new power operator?
Is there are better way to define the "identity function" result for
f ⍣ 0
?
Reference links
Here is a description of APL's ⍣
:
"Power Operator".
(⍣
is a "star with two dots", or more formally "Apl Functional Symbol Star Diaeresis".)
Attempt
Here is an attempt of an implementation:
sub infix:<⍣>( &func, Int:D $times where $times >= 0 ) {
if $times == 0 {
sub func2($d) {$d}
} else {
sub func2($d) {
my $res = &func($d);
for 2..$times -> $t { $res = &func($res) }
$res
}
}
}
sub plus1(Int:D $i){ $i + 1 }
say 'Using (&plus1 ⍣ 0) over 4 :';
say (&plus1 ⍣ 0)(4);
say 'Using (&plus1 ⍣ 10) over 4 :';
say (&plus1 ⍣ 10)(4);
# Using (&plus1 ⍣ 0) over 4 :
# 4
# Using (&plus1 ⍣ 10) over 4 :
# 14
(I followed this tutorial page: https://docs.raku.org/language/optut .)
Tests
The definition provided by Brad Gilbert's answer passes all of the tests below.
use Test;
sub infix:<⍣> ( &func, UInt $repeat ) is tighter( &[∘] ) { [∘] &func xx $repeat }
proto plus1(|) {*}
multi plus1(Int:D $i){ $i + 1 }
multi plus1(Bool:D $b){ $b.Int + 1 }
multi plus1(Int:D $i, Bool:D $b){ $i + $b.Int + 1 }
multi plus1(Int:D $i, Int:D $j){ $i + $j + 1 }
multi plus1(@j){ ([+] @j) + 1}
multi plus1(Int:D $i, @j){ plus1($i) + plus1(@j) - 1 }
multi plus1(%h){ ([+] %h.values) + 1 }
plan 9;
is plus1([1, 3, 5, 3]), 13, 'plus1([1, 3, 5, 3])';
is plus1(3, [1, 3, 5, 3]), 16, 'plus1(3, [1, 3, 5, 3])';
is plus1(3, True), 5, 'plus1(3, True)';
is (&plus1 ⍣ 0)(4), 4, '(&plus1 ⍣ 0)(4)';
is (&plus1 ⍣ 10)(4), 14, '(&plus1 ⍣ 10)(4)';
is (&plus1 ⍣ 10)([1, 3, 5, 3]), 22, '(&plus1 ⍣ 10)([1, 3, 5, 3])';
is (&plus1 ⍣ 3)(4, True), 8, '(&plus1 ⍣ 3)(4, True)';
is (&plus1 ⍣ 3)(3, [1, 3, 5, 3]), 18, '(&plus1 ⍣ 3)(3, [1, 3, 5, 3])';
is (&plus1 ⍣ 3)({a => 1, b => 3, c => 5}), 12, '(&plus1 ⍣ 3)({a => 1, b => 3, c => 5})';
done-testing;