In Ruby I can group together some lines of code like so with a begin block:
x = begin
puts "Hi!"
a = 2
b = 3
a + b
end
puts x # 5
it's immediately evaluated and its value is the last value of the block (a + b here) (Javascripters do a similar thing with IIFEs)
What are the ways to do this in Raku? Is there anything smoother than:
my $x = ({
say "Hi!";
my $a = 2;
my $b = 3;
$a + $b;
})();
say $x; # 5
Insert a do in front of the block. This tells Raku to:
Immediately do whatever follows the do on its right hand side;
Return the value to the do's left hand side:
my $x = do {
put "Hi!";
my $a = 2;
my $b = 3;
$a + $b;
}
That said, one rarely needs to use do.
Instead, there are many other IIFE forms in Raku that just work naturally without fuss. I'll mention just two because they're used extensively in Raku code:
with whatever { .foo } else { .bar }
You might think I'm being silly, but those are two IIFEs. They form lexical scopes, have parameter lists, bind from arguments, the works. Loads of Raku constructs work like that.
In the above case where I haven't written an explicit parameter list, this isn't obvious. The fact that .foo is called on whatever if whatever is defined, and .bar is called on it if it isn't, is both implicit and due to the particular IIFE calling behavior of with.
See also if, while, given, and many, many more.
What's going on becomes more obvious if you introduce an explicit parameter list with ->:
for whatever -> $a, $b { say $a + $b }
That iterates whatever, binding two consecutive elements from it to $a and $b, until whatever is empty. If it has an odd number of elements, one might write:
for whatever -> $a, $b? { say $a + $b }
And so on.
Bottom line: a huge number of occurrences of {...} in Raku are IIFEs, even if they don't look like it. But if they're immediately after an =, Raku defaults to assuming you want to assign the lambda rather than immediately executing it, so you need to insert a do in that particular case.
Welcome to Raku!
my $x = BEGIN {
say "Hi!";
my $a = 2;
my $b = 3;
$a + $b;
}
I guess the common ancestry of Raku and Ruby shows :-)
Also note that to create a constant, you can also use constant:
my constant $x = do {
say "Hi!";
my $a = 2;
my $b = 3;
$a + $b;
}
If you can have a single statement, you can leave off the braces:
my $x = BEGIN 2 + 3;
or:
my constant $x = 2 + 3;
Regarding blocks: if they are in sink context (similar to "void" context in some languages), then they will execute just like that:
{
say "Executing block";
}
No need to explicitely call it: it will be called for you :-)
Related
I was looking at REPL-like evaluation of code from here and here, and tried to make a very small version for it, yet it fails:
use nqp;
class E {
has Mu $.compiler;
has $!save_ctx;
method evaluate(#fragments) {
for #fragments -> $code {
my $*MAIN_CTX;
my $*CTXSAVE := self;
$!compiler.eval($code,
outer_ctx => nqp::ctxcaller(nqp::ctx()));
if nqp::defined($*MAIN_CTX) {
$!save_ctx := $*MAIN_CTX;
}
}
}
method ctxsave(--> Nil) {
say "*in ctxsave*";
$*MAIN_CTX := nqp::ctxcaller(nqp::ctx());
$*CTXSAVE := 0;
}
}
my $e := E.new(compiler => nqp::getcomp("Raku"));
nqp::bindattr($e, E, '$!save_ctx', nqp::ctx());
$e.evaluate: ('say my #vals = 12, 3, 4;', 'say #vals.head');
I pieced together this from the above links without very much knowing what I'm doing :) When run, this happens:
*in ctxsave*
[12 3 4]
===SORRY!=== Error while compiling file.raku
Variable '#vals' is not declared. Did you mean '&val'?
file.raku:1
------> say ⏏#vals.head
with Rakudo v2022.04. First fragment was supposed to declare it (and prints it). Is it possible to do something like this, so it recognizes #vals as declared?
You can do it in pure Raku code, although depending on the not-exactly-official context parameter to EVAL.
# Let us use EVAL with user input
use MONKEY;
loop {
# The context starts out with a fresh environment
state $*REPL-CONTEXT = UNIT::;
# Get the next line of code to run.
my $next-code = prompt '> ';
# Evaluate it; note that exceptions with line numbers will be
# off by one, so may need fixups.
EVAL "\q'$*REPL-CONTEXT = ::;'\n$next-code", context => $*REPL-CONTEXT;
}
Trying it out:
$ raku simple-repl.raku
> my $x = 35;
> say $x;
35
> my $y = 7;
> say $x + $y;
42
The documentation says that the comma operator has higher precedence than the assignment = operator, and this is specifically different than in Perl, so that we are allowed to remove parentheses in some contexts.
This allows us to do things like this:
my #array = 1, 2, 3;
What I don't understand is why when do something like this:
sub test() { return 1, 2 }
my ($a, $b);
$a, $b = test();
$b get assigned [1 2] while $a gets no value.
While I would assume that the following would be equivalent, because the comma operator is tighter than the assignment.
($a, $b) = test();
The semantics of Raku have a lot of subtlety and I guess I am thinking too much in terms of Perl.
Like raiph said in the comments, my original assumption that the comma operator has higher precedence than the assignment operator was false. And it was due to a problem in the rendering of the operator precedence table, which didn't presented operators in their precedence order.
This explains the actual behavior of Raku for my examples.
The = operator itself is always item assignment level, which is tighter than comma. However, it may apply a "sub-precedence", which is how it is compared to any further infixes in the expression that follow it. The term that was parsed prior to the = is considered, and:
In the case that the assignment is to a Scalar variable, then the = operator works just like any other item assignment precedence operator, which is tighter than the precedence of ,
In any other case, its precedence relative to following infixes is list prefix, which is looser than the precedence of ,
To consider some cases (first, where it doesn't impact anything):
$foo = 42; # $ sigil, so item assignment precedence after
#foo = 1; # # sigil, so list assignment precedence after
#foo[0] = 1; # postcircumfix (indexing operator) means list assignment after...
$foo[0] = 1; # ...always, even in this case
If we have a single variable on the left and a list on the right, then:
#foo = 1, 2; # List assignment into #foo two values
$foo = 1, 2; # Assignment of 1 into $foo, 2 is dead code
These apply with the = initializer (following a my $var declaration). This means that:
loop (my $i = 0, my $j = $end; $i < $end; $i++, $j--) {
...
}
Will result in $i being assigned 0 and $j being assigned $end.
Effectively, the rule means we get to have parentheses-free initialization of array and hash variables, while still having lists of scalar initializations work out as in the loop case.
Turning to the examples in the question. First, this:
($a, $b) = test();
Parses a single term, then encounters the =. The precedence when comparing any following infixes would be list prefix (looser than ,). However, there are no more infixes here, so it doesn't really matter.
In this case:
sub test() { return 1, 2 }
my ($a, $b);
$a, $b = test();
The precedence parser sees the , infix, followed by the = infix. The = infix in itself is tighter than comma (item assignment precedence); the sub-precedence is only visible to infixes parsed after the = (and there are none here).
Note that were it not this way, and the precedence shift applied to the expression as a whole, then:
loop (my $i = 0, my #lagged = Nil, |#values; $i < #values; $i++) {
...
}
Would end up grouped not as (my $i = 0), (my #lagged = Nil, |#values), but rather (my $i = 0, my #lagged) = Nil, |#values, which is rather less useful.
In Raku a scalar might be immutable or it might be an actual variable:
my $a := 6; # constant
my $b = 6; # variable
Is there a programmatic way to check whether a scalar is immutable without just trying to change it and seeing if it works?
First, a slight terminological correction (not to be nitpicky, but just because this area is a bit tricky and being precise with our terms can help).
It's not really right to say that my $a := 6 is a constant; that expression binds $a to the value 6, which prevents you from assigning a different value to $a (with the = operator). However, you can still rebind a new value to $a (with the := operator). This means that $a can still, in a sense, be mutated – or, at least, can be made to point to something new. For a truly constant $a, you should use either constant $a or change $a to a sigil-less variable (my \a = 6).
Now to the actual answer to your question: To determine whether $a is bound or assigned to a value, you can use $a.VAR.WHAT. If $a is assigned a value, this will return the type of container, (Scalar); if it is bound, then it will return the type of the bound value.
As far as I know, there isn't a way to tell the difference between an $a bound to a value and one that's a constant, though I would love to be wrong about that.
The code below illustrates what I just said:
my $a = 1;
say $a.VAR.WHAT; # OUTPUT: «(Scalar)»
$a = 2;
say $a; # OUTPUT: «2»
my $b := 1;
say $b.VAR.WHAT;# OUTPUT: «(Int)»
$b := 2;
say $b; # OUTPUT: «2»
constant $c = 1;
say $c.VAR.WHAT; # OUTPUT: «(Int)»
# $c := 2; ILLEGAL
say $c; # OUTPUT: «1»
Another way, using multiple dispatch:
my $a := 6; # constant
my $b = 6; # variable
multi sub mutable($ is rw) { True }
multi sub mutable($) { False }
say mutable($a); # False
say mutable($b); # True
say mutable(42); # False
Identifier terms are defined in the documentation alongside constants, with pretty much the same use case, although terms compute their value in run time while constants get it in compile time. Potentially, that could make terms use global variables, but that's action at a distance and ugly, so I guess that's not their use case.
OTOH, they could be simply routines with null signature:
sub term:<þor> { "Is mighty" }
sub Þor { "Is mighty" }
say þor, Þor;
But you can already define routines with null signature. You can sabe, however, the error when you write:
say Þor ~ Þor;
Which would produce a many positionals passed; expected 0 arguments but got 1, unlike the term. That seems however a bit farfetched and you can save the trouble by just adding () at the end.
Another possible use case is defying the rules of normal identifiers
sub term:<✔> { True }
say ✔; # True
Are there any other use cases I have missed?
Making zero-argument subs work as terms will break the possibility to post-declare subs, since finding a sub after having parsed usages of it would require re-parsing of earlier code (which the perl 6 language refuses to do, "one-pass parsing" and all that) if the sub takes no arguments.
Terms are useful in combination with the ternary operator:
$ perl6 -e 'sub a() { "foo" }; say 42 ?? a !! 666'
===SORRY!=== Error while compiling -e
Your !! was gobbled by the expression in the middle; please parenthesize
$ perl6 -e 'sub term:<a> { "foo" }; say 42 ?? a !! 666'
foo
Constants are basically terms. So of course they are grouped together.
constant foo = 12;
say foo;
constant term:<bar> = 36;
say bar;
There is a slight difference because term:<…> works by modifying the parser. So it takes precedence.
constant fubar = 38;
constant term:<fubar> = 45;
say fubar; # 45
The above will print 45 regardless of which constant definition comes first.
Since term:<…> takes precedence the only way to get at the other value is to use ::<fubar> to directly access the symbol table.
say ::<fubar>; # 38
say ::<term:<fubar>>; # 45
There are two main use-cases for term:<…>.
One is to get a subroutine to be parsed similarly to a constant or sigilless variable.
sub fubar () { 'fubar'.comb.roll }
# say( fubar( prefix:<~>( 4 ) ) );
say fubar ~ 4; # ERROR
sub term:<fubar> () { 'fubar'.comb.roll }
# say( infix:<~>( fubar, 4 ) );
say fubar ~ 4;
The other is to have a constant or sigiless variable be something other than an a normal identifier.
my \✔ = True; # ERROR: Malformed my
my \term:<✔> = True;
say ✔;
Of course both use-cases can be combined.
sub term:<✔> () { True }
Perl 5 allows subroutines to have an empty prototype (different than a signature) which will alter how it gets parsed. The main purpose of prototypes in Perl 5 is to alter how the code gets parsed.
use v5;
sub fubar () { ord [split('','fubar')]->[rand 5] }
# say( fubar() + 4 );
say fubar + 4; # infix +
use v5;
sub fubar { ord [split('','fubar')]->[rand 5] }
# say( fubar( +4 ) );
say fubar + 4; # prefix +
Perl 6 doesn't use signatures the way Perl 5 uses prototypes. The main way to alter how Perl 6 parses code is by using the namespace.
use v6;
sub fubar ( $_ ) { .comb.roll }
sub term:<fubar> () { 'fubar'.comb.roll }
say fubar( 'zoo' ); # `z` or `o` (`o` is twice as likely)
say fubar; # `f` or `u` or `b` or `a` or `r`
sub prefix:<✔> ( $_ ) { "checked $_" }
say ✔ 'under the bed'; # checked under the bed
Note that Perl 5 doesn't really have constants, they are just subroutines with an empty prototype.
use v5;
use constant foo => 12;
use v5;
sub foo () { 12 } # ditto
(This became less true after 5.16)
As far as I know all of the other uses of prototypes have been superseded by design decisions in Perl 6.
use v5;
sub foo (&$) { $_[0]->($_[1]) }
say foo { 100 + $_[0] } 5; # 105;
That block is seen as a sub lambda because of the prototype of the foo subroutine.
use v6;
# sub foo ( &f, $v ) { f $v }
sub foo { #_[0].( #_[1] ) }
say foo { 100 + #_[0] }, 5; # 105
In Perl 6 a block is seen as a lambda if a term is expected. So there is no need to alter the parser with a feature like a prototype.
You are asking for exactly one use of prototypes to be brought back even though there is already a feature that covers that use-case.
Doing so would be a special-case. Part of the design ethos of Perl 6 is to limit the number of special-cases.
Other versions of Perl had a wide variety of special-cases, and it isn't always easy to remember them all.
Don't get me wrong; the special-cases in Perl 5 are useful, but Perl 6 has for the most part made them general-cases.
Infinite lazy lists are awesome!
> my #fibo = 0, 1, *+* ... *;
> say #fibo[1000];
43466557686937456435688527675040625802564660517371780402481729089536555417949051890403879840079255169295922593080322634775209689623239873322471161642996440906533187938298969649928516003704476137795166849228875
They automatically cache their values, which is handy ... most of the time.
But when working with huge Fibonacci numbers (example), this can cause memory issues.
Unfortunately, I can't figure out how to create a non-caching Fibonacci sequence. Anyone?
One major problem is you are storing it in an array, which of course keeps all of its values.
The next problem is a little more subtle, the dotty sequence generator syntax LIST, CODE ... END doesn't know how many of the previous values the CODE part is going to ask for, so it keeps all of them.
( It could look at the arity/count of the CODE, but it doesn't currently seem to from experiments at the REPL )
Then there is the problem that using &postcircumfix:<[ ]> on a Seq calls .cache on the assumption that you are going to ask for another value at some point.
( From looking at the source for Seq.AT-POS )
It's possible that a future implementation could be better at each of these drawbacks.
You could create the sequence using a different feature to get around the current limitations of the dotty sequence generator syntax.
sub fibonacci-seq (){
gather {
take my $a = 0;
take my $b = 1;
loop {
take my $c = $a + $b;
$a = $b;
$b = $c;
}
}.lazy
}
If you are just iterating through the values you can just use it as is.
my $v;
for fibonacci-seq() {
if $_ > 1000 {
$v = $_;
last;
}
}
say $v;
my $count = 100000;
for fibonacci-seq() {
if $count-- <= 0 {
$v = $_;
last;
}
}
say chars $v; # 20899
You could also use the Iterator directly. Though this isn't necessary in most circumstances.
sub fibonacci ( UInt $n ) {
# have to get a new iterator each time this is called
my \iterator = fibonacci-seq().iterator;
for ^$n {
return Nil if iterator.pull-one =:= IterationEnd;
}
my \result = iterator.pull-one;
result =:= IterationEnd ?? Nil !! result
}
If you have a recent enough version of Rakudo you can use skip-at-least-pull-one.
sub fibonacci ( UInt $n ) {
# have to get a new iterator each time this is called
my \result = fibonacci-seq().iterator.skip-at-least-pull-one($n);
result =:= IterationEnd ?? Nil !! result
}
You can also implement the Iterator class directly, wrapping it in a Seq.
( this is largely how methods that return sequences are written in the Rakudo core )
sub fibonacci-seq2 () {
Seq.new:
class :: does Iterator {
has Int $!a = 0;
has Int $!b = 1;
method pull-one {
my $current = $!a;
my $c = $!a + $!b;
$!a = $!b;
$!b = $c;
$current;
}
# indicate that this should never be eagerly iterated
# which is recommended on infinite generators
method is-lazy ( --> True ) {}
}.new
}
Apparently, a noob cannot comment.
When defining a lazy iterator such as sub fibonacci-seq2, one should mark the iterator as lazy by adding a "is-lazy" method that returns True, e.g.:
method is-lazy(--> True) { }
This will allow the system to detect possible infiniloops better.