Got this:
#! /usr/bin/env raku
use v6;
multi blah (#arg) { #arg = 7; }
multi blah ($arg) { $arg = '3'; }
my #array = 1, 2, 3;
blah #array;
say #array;
my $string = 'onetwothree';
blah $string;
say $string;
Get this:
[7]
Cannot assign to a readonly variable or a value
in sub blah at ./chop.raku line 5
in block <unit> at ./chop.raku line 12
I found this behavior surprising, particularly the ability to change to the array outside of the scope of the function.
Can someone please explain why I can change an array argument passed to a function but not a scalar? And is there a way to make arrays read only when passed to a function? Is there any way to make a scalar passed to a function mutable?
Can someone please explain why I can change an array argument passed to a function but not a scalar?
Arguments passed to a function are read-only (by default; more on that below) and so generally can't be changed. The reason that you can modify the Array is that Arrays are, themselves, mutable (see the Lists, sequences, and arrays page in the docs for details). This means that, even though you can't change the Array itself, you can still change the values in the Array.
And is there a way to make arrays read only when passed to a function?
The List is Raku's immutable type for positional data (sort of; it's not deeply immutable, but that's beyond the scope here). If you pass a List into a function, the function won't be able to modify the contents of that list. For example, this code throws an error:
my #l is List = (1, 2, 3);
sub f(#var) { #var[1] = 42 }
f #l;
Is there any way to make a scalar passed to a function mutable?
There are two ways, depending on what sort of mutability you want: you can use either the is rw and is copy traits. For example, you might declare a function like so:
sub f($a is copy, $b is rw) {...}
That declaration allows &f to modify both $a and $b. But for $a, &f is modifying its own local copy and won't have any effect on the value its callers see for $a. With $b, however, &f is modifying shared state and any changes it makes to $b will be visible even outside the scope of &f.
Related
I've written a code to calculate the Fibonacci series using array variables inside the explicit generator like this:
my #fib = [0],[1],-> #a, #b {[|#a Z+ |#b]} ... Inf;
say #fib[^6];
This works as expected. But when I use scalar variables inside the same code, it works too:
my #fib_v2 = [0],[1],-> $a, $b {[|$a Z+ |$b]} ... Inf;
say #fib_v2[^6];
Could they be called scalar variables pointing to the arrays? What are they called when they are used in this manner?
Note that I've browsed the online Raku documentation but it's hard to spot that particular information i.e. if arrays can be referred using scalar variables.
You should say that "the scalar variables are bound to the arrays". Because that is what happens. You could think of:
-> $a, $b { say $a; say $b }("foo", "bar")
as:
{ my $a := "foo"; my $b := "bar"; say $a; say $b }
So, when you bind an array to a scalar, it is still an Array object. And calling methods on it, will just work. It may just make reading the code more difficult.
Note this is different from assigning an array to a scalar.
raku -e 'my $a := [1,2,3]; .say for $a'
1
2
3
versus:
raku -e 'my $a = [1,2,3]; .say for $a'
[1 2 3]
The for iterating logic sees the container in $a and takes that as a sign to NOT iterate over it, even though it contains an Iterable.
Note that I've browsed the online Raku documentation but it's hard to spot that particular information i.e. if arrays can be referred using scalar variables.
It basically falls out of correctly grokking the relation between containers and binding. About 4 years ago, I wrote a blog post about it: https://opensource.com/article/18/8/containers-perl-6 Please pardon the archaic naming :-)
I'm trying to map the chunks from a Match object to an array of Pairs with mutable string values -which I suppose requires scalarizing the strings. The only way I could figure out how to do this by putting them in square brackets and indexing them:
my #n =map { $_.key => [$_.value.Str][0] }, G::parse($str).chunks;
Scalarizing with $(..) didn't work nor did cloning and such. There must be a more canonical way. And why didn't $(..) work -isn't that what it's for?
A few things to unpick here:
All strings in P6 are immutable. (Thanks Liz. :)) But you probably don't mean you want to get a mutable string. You probably just mean having strings in containers so the contents of those containers can be changed.
The => pair constructor does not decontainerize its right hand value if it's in a container. So if bar is a Scalar container that contains a string then foo => bar constructs a pair with its value being that Scalar container containing that string.
$(...) is used to parenthesize an expression that is to be treated as singular even if is a plural container. (This mirrors #(...) which is used to parenthesize an expression that is to be treated as plural even if it is a singular container or value.)
It's not surprising that you thought $(...) would construct a Scalar container. (After all, %(...) constructs a Hash, so why not?) But instead you must use a Scalar declarator.
The most succinct Scalar declarator is for an anonymous state Scalar using $ = .... But #Håkon has used my $ = ... in their answer. Why? Because the {...} closure called by the map retains state between calls of it. If you use just $ = ... then you'd be reusing the same Scalar container for all the pairs. Instead you need my $ = ... to get fresh Scalars for each pair's value.
You can put the immutable string into a scalar container by doing:
my #n = map { $_.key => my $ = $_.value.Str }, G::parse($str).chunks;
then you can later modify the content of the scalar container (but not the content of the string):
#n[0].value = "Hello";
This example is taken from roast, although it's been there for 8 years:
role doc { has $.doc is rw }
multi trait_mod:<is>(Variable $a, :$docced!) {
$a does doc.new(doc => $docced);
}
my $dog is docced('barks');
say $dog.VAR;
This returns Any, without any kind of role mixed in. There's apparently no way to get to the "doc" part, although the trait does not error. Any idea?
(This answer builds on #guifa's answer and JJ's comment.)
The idiom to use in variable traits is essentially $var.var.VAR.
While that sounds fun when said aloud it also seems crazy. It isn't, but it demands explanation at the very least and perhaps some sort of cognitive/syntactic relief.
Here's the brief version of how to make some sense of it:
$var makes sense as the name of the trait parameter because it's bound to a Variable, a compiler's-eye view of a variable.
.var is needed to access the user's-eye view of a variable given the compiler's-eye view.
If the variable is a Scalar then a .VAR is needed as well to get the variable rather than the value it contains. (It does no harm if it isn't a Scalar.)
Some relief?
I'll explain the above in more detail in a mo, but first, what about some relief?
Perhaps we could introduce a new Variable method that does .var.VAR. But imo this would be a mistake unless the name for the method is so good it essentially eliminates the need for the $var.var.VAR incantation explanation that follows in the next section of this answer.
But I doubt such a name exists. Every name I've come up with makes matters worse in some way. And even if we came up with the perfect name, it would still barely be worth it at best.
I was struck by the complexity of your original example. There's an is trait that calls a does trait. So perhaps there's call for a routine that abstracts both that complexity and the $var.var.VAR. But there are existing ways to reduce that double trait complexity anyway, eg:
role doc[$doc] { has $.doc is rw = $doc}
my $dog does doc['barks'];
say $dog.doc; # barks
A longer explanation of $var.var.VAR
But $v is already a variable. Why so many var and VARs?
Indeed. $v is bound to an instance of the Variable class. Isn't that enough?
No, because a Variable:
Is for storing metadata about a variable while it's being compiled. (Perhaps it should have been called Metadata-About-A-Variable-Being-Compiled? Just kidding. Variable looks nice in trait signatures and changing its name wouldn't stop us needing to use and explain the $var.var.VAR idiom anyway.)
Is not the droid we are looking for. We want a user's-eye view of the variable. One that's been declared and compiled and is then being used as part of user code. (For example, $dog in the line say $dog.... Even if it were BEGIN say $dog..., so it ran at compile-time, $dog would still refer to a symbol that's bound to a user's-eye view container or value. It would not refer to the Variable instance that's only the compiler's-eye view of data related to the variable.)
Makes life easier for the compiler and those writing traits. But it requires that a trait writer accesses the user's-eye view of the variable to access or alter the user's-eye view. The .var attribute of the Variable stores that user's-eye view. (I note the roast test has a .container attribute that you omitted. That's clearly now been renamed .var. My guess is that that's because a variable may be bound to an immutable value rather than a container so the name .container was considered misleading.)
So, how do we arrive at $var.var.VAR?
Let's start with a variant of your original code and then move forward. I'll switch from $dog to #dog and drop the .VAR from the say line:
multi trait_mod:<is>(Variable $a, :$docced!) {
$a does role { has $.doc = $docced }
}
my #dog is docced('barks');
say #dog.doc; # No such method 'doc' for invocant of type 'Array'
This almost works. One tiny change and it works:
multi trait_mod:<is>(Variable $a, :$docced!) {
$a.var does role { has $.doc = $docced }
}
my #dog is docced('barks');
say #dog.doc; # barks
All I've done is add a .var to the ... does role ... line. In your original, that line is modifying the compiler's-eye view of the variable, i.e. the Variable object bound to $a. It doesn't modify the user's-eye view of the variable, i.e. the Array bound to #dog.
As far as I know everything now works correctly for plural containers like arrays and hashes:
#dog[1] = 42;
say #dog; # [(Any) 42]
say #dog.doc; # barks
But when we try it with a Scalar variable:
my $dog is docced('barks');
we get:
Cannot use 'does' operator on a type object Any.
This is because the .var returns whatever it is that the user's-eye view variable usually returns. With an Array you get the Array. But with a Scalar you get the value the Scalar contains. (This is a fundamental aspect of P6. It works great but you have to know it in these sorts of scenarios.)
So to get this to appear to work again we have to add a couple .VAR's as well. For anything other than a Scalar a .VAR is a "no op" so it does no harm to cases other than a Scalar to add it:
multi trait_mod:<is>(Variable $a, :$docced!) {
$a.var.VAR does role { has $.doc = $docced }
}
And now the Scalar case also appears to work:
my $dog is docced('barks');
say $dog.VAR.doc; # barks
(I've had to reintroduce the .VAR in the say line for the same reason I had to add it to the $a.var.VAR ... line.)
If all were well that would be the end of this answer.
A bug
But something is broken. If we'd attempted to initialize the Scalar variable:
my $dog is docced('barks') = 42;
we'd see:
Cannot assign to an immutable value
As #guifa noted, and I stumbled on a while back:
It seems that a Scalar with a mixin no longer successfully functions as a container and the assignment fails. This currently looks to me like a bug.
Not a satisfactory answer but maybe you can progress from it
role doc {
has $.doc is rw;
}
multi trait_mod:<is>(Variable:D $v, :$docced!) {
$v.var.VAR does doc;
$v.var.VAR.doc = $docced;
}
say $dog; # ↪︎ Scalar+{doc}.new(doc => "barks")
say $dog.doc; # ↪︎ barks
$dog.doc = 'woofs'; #
say $dog; # ↪︎ Scalar+{doc}.new(doc => "woofs")
Unfortunately, there is something off with this, and applying the trait seems to cause the variable to become immutable.
Starting with the example in the Iterable doc page
role DNA does Iterable {
method iterator(){ self.comb.iterator }
};
my #a does DNA = 'GAATCC';
.say for #a; # OUTPUT: «GAATCC»
I found it weird it's declared using the #, so I changed it to the natural way of declaring strings, $:
my $a does DNA = 'GAATCC';
But that fails with a somewhat bewildering "Cannot assign to an immutable value". No need to assign on the spot, so we can do:
my $a = 'GAATCC';
$a does DNA;
.say for $a;
Which just leaves mixing-in for later. But that just prints the string, without paying any attention to the Iterable mixin. Let's call it then explicitly:
.say for $a.iterator;
it does kinda the same thing as before, only it prints the value of $a.iterator, without actually calling the function:
<anon|69>.new
This looks like the same thing it's going on in this other question. Baseline question is I don't understand what role Iterable really does, and what for really does and when it is calling iterator on some object. Any idea?
I don't think this line does what you think it does:
my #a does DNA = 'GAATCC';
It is the same as:
my #a := [ 'GAATCC', ];
#a does DNA;
Basically the .comb call coerces the array into a Str, and splits that into characters.
If you instead did this:
my #a = 'GAATCC' but DNA;
Which is basically the same as
my #a := Seq.new(('GAATCC' but DNA).iterator).Array;
Note that # variables store Positional values not Iterable values.
The thing you want is
my $a = 'GAATCC' but DNA;
$a.map: &say;
If you want to be able to use for you can't use a variable with a $ sigil
my \a = 'GAATCC' but DNA;
.say for a;
You may want to add Seq list List etc methods to DNA.
role DNA does Iterable {
method iterator(){
self.comb.iterator
}
method Seq(){
Seq.new: self.iterator
}
method list(){
# self.Seq.list
List.from-iterator: self.iterator
}
}
my $a = 'GAATCC' but DNA;
.say for #$a;
Your question's title points to a bug. This answer covers the bug and also other implicit and explicit questions you asked.
Background
fails with a somewhat bewildering "Cannot assign to an immutable value".
I think that's a bug. Let's start with some code that works:
my $a = 42;
say $a; # 42
say WHAT $a; # (Int) type of VALUE currently ASSIGNED to $a
say WHAT VAR $a; # (Scalar) type of VARIABLE currently BOUND to $a
$a = 42; # works fine
In the my declaraton $a gets BOUND to a new Scalar container. A Scalar container normally hides itself. If you ask WHAT type $a is, you actually get the type of the value currently ASSIGNED to the Scalar (the value it "contains"). You need VAR to access the container BOUND to $a. When you assign with = to a Scalar container you copy the assigned value into the container.
role foo {}
$a does foo; # changes the VALUE currently ASSIGNED to $a
# (NOT the VARIABLE that is BOUND to $a)
say $a; # 42 mixed in `foo` role is invisible
say WHAT $a; # (Int+{foo}) type of VALUE currently ASSIGNED to $a
say WHAT VAR $a; # (Scalar) type of VARIABLE currently BOUND to $a
$a = 99; say $a; # 99
The does mixes the foo role into the 42. You can still assign to $a because it's still bound to a Scalar.
Note how these two uses of does have very different effects:
my $a does foo; # mixes `foo` into VARIABLE bound to $a
$a does foo; # mixes `foo` into VALUE assigned to $a
The bug
$a.VAR does foo; # changes VARIABLE currently BOUND to $a (and it loses the 42)
say $a; # Scalar+{foo}.new VALUE currently ASSIGNED to $a
say WHAT $a; # (Scalar+{foo}) type of VALUE currently ASSIGNED to $a
say WHAT VAR $a; # (Scalar+{foo}) type of VARIABLE currently BOUND to $a
$a = 'uhoh'; # Cannot assign to an immutable value
The does mixes the foo role into the Scalar bound to $a. It seems that a Scalar with a mixin no longer successfully functions as a container and the assignment fails.
This currently looks to me like a bug.
my $b does foo; # BINDS mixed in VARIABLE to $b
$b = 'uhoh'; # Cannot assign to an immutable value
my $b does foo has the same result as my $b; $b.VAR does foo; so you get the same problem as above.
Other things you were confused about
my $a = 'GAATCC';
$a does DNA;
.say for $a;
just prints the string, without paying any attention to the Iterable mixin.
Because the $a VARIABLE is still bound to a Scalar (as explained in the Background section above), the VALUE that now has a DNA role mixed in is irrelevant per the decision process for uses about whether to call its argument's .iterator method.
Let's call it then explicitly ... prints the value of $a.iterator, without actually calling the function:
.say for $a.iterator;
Well it does call your DNA role's .iterator method. But that has another .iterator call at the end of the self.comb returned by your DNA role's iterator method so you're .saying that secondary .iterator.
Solutions that work today
I think Brad's answer nicely covers most of your options.
And I think your nice does DNA gist is as good as it gets with today's P6 if you want to use the $ sigil.
A solution that might one day work
In an ideal world all the sweetness in the P6 design would be fully realized in 6.c and the Rakudo compiler implementation of Perl 6. Perhaps that would include the ability to write this and get what you want:
class DNA is Scalar does Iterable { ... }
my $a is DNA = 'GAATCC';
.say for $a;
The ... code would be about the same as what you have in your gist except that the DNA class would be a scalar container, and so the new method would instead be a STORE method or similar that would assign the passed value to a $!value attribute or some such when a value was assigned to the container using =.
But instead, you get:
is trait on $-sigil variable not yet implemented. Sorry.
So the closest you can get today to the ideal of = 'string' to change $a is to bind using := DNA.new('string') as you did in your gist.
Note that you can bind arbitrary composite containers to # and % sigil variables. So you can see how things are supposed to eventually work.
I want to assign literals to some of the variables at the end of the file with my program, but to use these variables earlier. The only method I've come up with to do it is the following:
my $text;
say $text;
BEGIN {
$text = "abc";
}
Is there a better / more idiomatic way?
Just go functional.
Create subroutines instead:
say text();
sub text { "abc" }
UPDATE (Thanks raiph! Incorporating your feedback, including reference to using term:<>):
In the above code, I originally omitted the parentheses for the call to text, but it would be more maintainable to always include them to prevent the parser misunderstanding our intent. For example,
say text(); # "abc"
say text() ~ text(); # "abcabc"
say text; # "abc", interpreted as: say text()
say text ~ text; # ERROR, interpreted as: say text(~text())
sub text { "abc" };
To avoid this, you could make text a term, which effectively makes the bareword text behave the same as text():
say text; # "abc", interpreted as: say text()
say text ~ text; # "abcabc", interpreted as: say text() ~ text()
sub term:<text> { "abc" };
For compile-time optimizations and warnings, we can also add the pure trait to it (thanks Brad Gilbert!). is pure asserts that for a given input, the function "always produces the same output without any additional side effects":
say text; # "abc", interpreted as: say text()
say text ~ text; # "abcabc", interpreted as: say text() ~ text()
sub term:<text> is pure { "abc" };
Unlike Perl 5, in Perl 6 a BEGIN does not have to be a block. However, the lexical definition must be seen before it can be used, so the BEGIN block must be done before the say.
BEGIN my $text = "abc";
say $text;
Not sure whether this constitutes an answer to your question or not.
First, a rephrase of your question:
What options are there for succinctly referring to a variable (or constant etc.) whose initialization code appears further down in the same source file?
Post declare a routine
say foo;
sub foo { 'abc' }
When a P6 compiler parses an identifier that has no sigil, it checks to see if it has already seen a declaration of that identifier. If it hasn't, then it assumes that the identifier corresponds to a routine which will be declared later as a "listop" routine (which takes zero or more arguments) and moves on. (If its assumption turns out to be wrong, it fails the compilation.)
So you can use routines as if they were variables as described in Christopher Bottom's answer.
Autodeclare a variable on first use
strict is a "pragma" that controls how a P6 compiler reacts when it parses an as yet undeclared variable/constant that starts with a sigil.
P6 starts programs with strict mode switched on. This means that the compiler will insist on predeclaration of any sigil'd variable/constant. (By predeclaration I mean an explicit declaration that appears textually before the variable/constant is used.)
But you can write use strict or no strict to control whether the strict pragma is on or off in a given lexical scope, so this will work:
no strict;
say $text;
BEGIN {
$text = "abc";
}
Warning Having no strict in effect (which is unfortunately how most programming languages work) makes accidental misspelling of variable names a bigger nuisance than it is with use strict mode on.
Declare a variable explicitly in the same statement as its first use
You don't have to write a declaration as a separate statement. You can instead declare and use a variable in the same statement or expression:
say my $text;
BEGIN {
$text = "abc";
}
Warning If you repeat my $bar in the exact same lexical scope, the compiler will emit a warning. In contrast, say my $bar = 42; if foo { say my $bar = 99 } creates two distinct $bar variables without warning.
Initialize at run-time
The BEGIN phaser shown above runs at compile-time (after the my declaration, which also happens at compile-time, but before the say, which happens at run-time).
If you want to initialize variables/constants at run-time instead, use INIT instead:
say my $text;
INIT {
$text = "abc";
}
INIT code runs before any other run-time code, so the initialization still happens before the say gets executed.
Use a positronic (ym) variable
Given a literal interpretation of your question a "positronic" or ym variable would be yet another solution. (This feature is not built-in. I'm including it mostly because I encountered it after answering this question and think it belongs here, at the very least for entertainment value.)
Initialization and calculation of such a variable starts in the last statement using it and occurs backwards relative to the textual order of the code.
This is one of the several crazy sounding but actually working and useful concepts that Damian "mad scientist" Conway discusses in his 2011 presentation Temporally Quaquaversal Virtual Nanomachine Programming In Multiple Topologically Connected Quantum-Relativistic Parallel Spacetimes... Made Easy!.
Here's a link to the bit where he focuses on these variables.
(The whole presentation is a delight, especially if you're interested in physics; programming techniques; watching highly creative wunderkinds; and/or enjoy outstanding presentation skills and humor.)
Create a PS pragma?
In terms of coolness, the following pales in comparison to Damian's positronic variable feature that I just covered, but it's an idea I had while pondering this question.
Someone could presumably implement something like the following pragma:
use PS;
say $text;
BEGIN $text = 'abc';
This PS would lexically apply no strict and in addition require that, to avoid a compile-time error:
An auto-declared variable/constant must match up with a post declaration in a BEGIN or INIT phaser;
The declaration must include initialization if the first use (textually) of a variable/constant is not a binding or assignment.