Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Immutable (uncontainerized) elements in arrays created by deepmap

Tags:

raku

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.

like image 547
Nikola Benes Avatar asked Dec 19 '21 19:12

Nikola Benes


People also ask

Does map () method mutate an array of objects?

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.

What are the mutable operations of an array?

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.

How to mutate an array of objects in JavaScript?

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.

How do I mutate an array of event items?

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.


Video Answer


1 Answers

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: «1␤2␤3␤»
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.)

like image 138
codesections Avatar answered Oct 19 '22 08:10

codesections