I'm learning Raku as a passion project and I wanted to implement a simple fizzbuzz, why is join
only retaining buzz if I write lambdas with pointy block?
my $iif =-> $x,$y,$z {if $x {$y} else {$z}}
my $modToString =-> $x,$y,$z {$iif($x%%$y,$z,'')}
my $FB =-> $x {join($modToString($x,3,'fizz'),$modToString($x,5,'buzz'))}
my $logic =-> $x {$iif($FB($x),$FB($x),$x)}
say map(-> $x {$logic($x)}, 1..100)
$modToString(1,3,'fizz')
>
$modToString(3,3,'fizz')
> fizz
$modToString(3,5,'buzz')
>
$modToString(5,5,'buzz')
> buzz
If I trasform the pointy blocks variables into placeholder variables, Rakudo throws an error:
my $iif = {if $^x {$^y} else {$^z}};
my $modToString = {$iif($^x%%$^y,$^z,'')};
my $FB = {join($modToString($^x,3,'fizz'),$modToString($^x,5,'buzz'))}
my $logic = {$iif($FB($^x),$FB($^x),$^x)}
say map(-> $x {$logic($x)}, 1..100)
Too many positionals passed; expected 1 argument but got 3
in block at <unknown file> line 1
in block at <unknown file> line 1
in block at <unknown file> line 1
in block at <unknown file> line 1
in block <unit> at <unknown file> line 1
If I try to put the brackets around the join arguments it just outputs the numbers:
my $iif =-> $x,$y,$z {if $x {$y} else {$z}}
my $modToString =-> $x,$y,$z {$iif($x%%$y,$z,'')}
my $FB =-> $x {join(<$modToString($x,3,'fizz'),$modToString($x,5,'buzz')>)}
my $logic =-> $x {$iif($FB($x),$FB($x),$x)}
say map(-> $x {$logic($x)}, 1..100)
Why?
Edit
After @Silvio Mayolo's answer I understood the problem and he also pointed out that I wrote a pointless ternary operator!
I also typed join instead of the tilde! I'm happy to say that my fizzbuzz now works:
my $modToString =-> $x,$y,$z {$x%%$y??$z!!''}
my $FB =-> $x {$modToString($x,3,'fizz')~$modToString($x,5,'buzz')}
my $logic =-> $x {$FB($x)??$FB($x)!!$x}
say map(-> $x {$logic($x)}, 1..100)
Thank you!
CodePudding user response:
Because lots of things in Raku are blocks, even things that don't look like it. In particular, this includes the "argument" to control flow like if
.
if 1 { 2 } else { 3 }
We've actually written two blocks here. One is a constant function returning 2
and the other is a constant function returning 3
. Now, usually, this is transparent to us and the Raku engine is smart enough to compile those away. But they're still there. In fact, we can make them explicit. The following behaves identically to the above if
statement.
if 1 -> { 2 } else -> { 3 }
In your case, however, it ends up mattering. Anonymous arguments (the ones with the ^
twigil) bind to the innermost block. So you've written
{if $^x {$^y} else {$^z}}
and you intended that it be equivalent to
-> $x, $y, $z {if $x {$y} else {$z}}
but what you actually wrote was
-> $x {if $x -> $y {$y} else -> $z {$z}};
So you've really written a function of one argument and passed it three arguments, as the error message states.
As a broad rule, you can generally assume that anytime you see a {
, it either begins a hash literal or a block. And the latter always introduces a scope in which local variables and arguments can exist.
In your particular case, you can use the ternary ??!!
operator (this is the same thing as ?:
in most other languages like C or Java)
{$^x ?? $^y !! $^z}
This operator doesn't short-circuit and doesn't introduce blocks, so it'll work fine.