Ways to get mutable strings from a Match object's chunks? - raku

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";

Related

Weird behavior when set Array of Strings to HashMap in Kotlin

I'm tryna add to HashMap Array of Strings but instead of normal Array of String I see only address in memory of String in console.
val map = mapOf<String, Array<String>>()
val list = listOf("sport")
val array = list.toTypedArray()
map["key"] to array
And Array after this operation converts in smth like this — [Ljava.lang.String;#518ed9b4
But expected to see this kind of behavior:
map["key"] -> array("sport")
What's the problem might be with this sample of code?
Arrays in Java/Kotlin don't have a good automatic conversion to strings (technically, their implementation of toString()). What you see is your array, but instead of showing the contents, it only says it is an array of strings and shows the memory address.
To show the contents of an array you can use builtin contentToString() extension or wrap it into a list:
println(arrayOf("sport").contentToString())
println(arrayOf("sport").asList())
BTW, I believe there is a mistake in your example. map["key"] to array doesn't do anything, it should be probably map["key"] = array. Also, map in your example is read-only, you can't add items to it. However, as you already got to the point you print an array, I assume your real code is a little different.

Why are arrays passed as arguments mutable but scalars are not?

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.

Can you use the lookup function in terraform with map variables containing arrarys?

I'm using map variables and the lookup function to configure aws differently depending on the workspace selected. It works fine when the variable contains string but I can't get it to work with arrays and I'm not sure if it's possible
I've poured over the terraform documentation but can't see to sort it out. It looks like it can't be down with a map of arrays. Maybe someone has sorted through this issue
variable "cidr" {
type = "map"
default = {
"prod" = ["10.7.3.0/24","10.7.4.0/24"]
"test" = ["10.8.3.0/24","10.8.4.0/24"]
}
}
cidr = ${lookup(var.cidr, terraform.workspace)}"
lookup() can only be used with maps of
primitive types.
If you are using Terraform v0.12.0 or later, the idiomatic way to access one of the lists from your map of lists is to use the index syntax:
cidr = var.cidr[terraform.workspace]
You can also use the index syntax in Terraform v0.11 or earlier, but it must be wrapped in a template string because that is how we indicate to Terraform that we intend to use an expression in those older versions:
cidr = "${var.cidr[terraform.workspace]}"
The lookup function is for situations where you don't know if the given key is present and want to provide a default value to use instead if it is not. Although lookup with only two arguments is still supported for backward-compatibillity, it should generally be used only in its three-argument form in modern Terraform:
# (this particular default is likely not a good idea, but this
# is just to illustrate the syntax.)
cidr = lookup(var.cidr, terraform.workspace, ["0.0.0.0/0"])
Until Terraform 0.12.7, the lookup function is indeed restricted to only work with maps of primitive types. In Terraform 0.12.7 it was generalized to behave the same way as the index operator, but with the extra rule of returning the default value if the requested key isn't present.
As a side note, if you are using Terraform v0.12.0 or later then you can provide a more specific type constraint on that variable:
variable "cidr" {
type = map(list(string))
default = {
"prod" = ["10.7.3.0/24","10.7.4.0/24"]
"test" = ["10.8.3.0/24","10.8.4.0/24"]
}
}
By telling Terraform exactly what element types are expected for the list and map type, Terraform can automatically check the value provided by the caller to make sure it conforms, and report a type error if not. If you just write "map" then that's a legacy shorthand for map(any), in which case Terraform can only check that it's a map of any single type, not specifically what the element type must be. I'd recommend always using exact type constraints in Terraform 0.12.0 or later.

Mixing-in roles in traits apparently not working

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.

Erlang Looping through a list (or set) to process files

I want to create 16 directories in Erlang.
for ( create_dir("work/p" ++ A, where A is an element in a list [0, 1, ... f]) (sixteen number in hex notation).
I could of course write sixteen lines like: mkdir ("work/p0"), mkdir("work/p1") etc.
I have looked at lists:foreach. In the examples fun is used, is possible to define a function outside the loop and call it?
I am new to Erlang and used to C++ etc.
Yes, it's possible to define a (named) function outside the call to lists:foreach/2. Why would you, though? This is a case when an anonymous function is incredibly handy:
lists:foreach(fun(N) ->
file:make_dir(
filename:join("work", "p"++integer_to_list(N, 16)))
end, lists:seq(0, 15)).
The filename:join/2 call will use the appropriate directory separator to construct the string work/pN, where N is an integer in hex representation constructed using integer_to_list/2, which converts an integer to a string (list) in a given base (16).
lists:seq/2 is a friendly little function that returns the list [A,A+1,A+2,...,B-1,B] given A and B.
Note that you could just as well have used the list comprehension syntax here, but since we're applying functions to a list for the side-effects alone, I chose to stick with a foreach.
If you really want to define a separate function -- let's call it foo and assume it takes 42 arguments -- you can refer to it as fun foo/42 in your code. This expression evaluates to a function object that, like an anonymous function defined inline, can be passed to lists:foreach/2.