If I understand the Raku docs correctly, the elements of Arrays are always containerized, i.e. Scalars. However, the deepmap method seems to create (inner) Arrays with uncontainerized elements:
my @a = [1, [2, 3]];
my @b = @a.deepmap: *.clone;
say @b[0].VAR.^name; # Scalar, this is OK
say @b[1].^name; # Array, as expected
say @b[1][0].VAR.^name; # Int, why?
@b[0] = 4; # this works
@b[1][0] = 5; # error: Cannot assign to an immutable value
Why does this happen?
For context, I originally wanted to use .deepmap: *.clone
to create a deep copy, but I needed the copy to be mutable. I solved the problem by using @a.deepmap: { my $ = .clone }
, but I am still curious why this happens.
It clearly states, that map method is not going to mutate the existing array, but it rather creates the new array of mutated data. Everything works as expected until it comes to the array of objects, which technically is working as expected as well.
Immutable array operations Array has several mutable operations - push, pop, splice, shift, unshift, reverse and sort. Using them is usually causing side effects and bugs that are hard to track. That’s why it’s important to use an immutable way.
In order to mutate this array of objects, we have multiple approaches. But essentially you have to loop through the array of items and modify its state. To achieve that, I am normally using array’s map method implementation which accepts the callback function as the parameter, where all the magic happens.
Each event item has its own name and some metadata, where the type of the event is specified. In order to mutate this array of objects, we have multiple approaches. But essentially you have to loop through the array of items and modify its state.
If I understand the Raku docs correctly, the elements of Arrays are always containerized, i.e. Scalars.
That's almost correct, but not quite – Array initialization (i.e., with [1, 2]
) containerizes the values, but that doesn't mean that elements are always containerized. For example, you can explicitly bind a value to a position in an array.
Or, as you've discovered, you can wind up with a non-containerized value when creating an Array in an unusual way. Let's take a look at what deepmap
is doing here:
my @a = [1, [2, 3]];
@a.deepmap({.say; $_}); # OUTPUT: «123»
say @a.raku; # OUTPUT: «[1 [2 3]]»
What's going on? Well, deepmap
is recursively descending into the structure and calling the function on each leaf element (that's why it prints 2
instead of [2 3]
on the second iteration). And then it binds the result to the slot it was iterating over.
So, with .clone
, deepmap
goes down to the leaf (e.g., 2
) and calls .clone
on that value, gets 2
(an Int
) and binds that to the position in the Array.
It appears that what you wanted to happen was for .clone
to be called on [2 3]
, rather than 2
. If so, you could do that for lists with one level of nesting with (like above) with .map(*.clone)
; for more complex nesting, you can use duckmap
or tests inside a map
expression (or, as you discovered, call .clone
on the leaf values and add a Scalar
manually.)
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With