Ad

Unexpected Behaviours With Perl6\Raku Lambdas That Are Supposed To Be Equal (I Guess)

- 1 answer

I'm learning Perl6 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!

Ad

Answer

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.

Ad
source: stackoverflow.com
Ad