Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Assignment to a List Container Confusion

Tags:

raku

I may be suffering from brain fade, but according to the docs regarding Items and List assignment (https://docs.raku.org/language/variables#Item_and_list_assignment ),

Assignment to a List container (list-context) always triggers list assignment.

However that seems to conflict with what I get from the code (here reproduced in the raku repl)..

> my %syns-by-name = %(Bq => ["Bq", "becquerel", "becquerels"], C => ["C", "coulomb", "coulombs"],)
    {Bq => [Bq becquerel becquerels], C => [C coulomb coulombs]}
> my Str @res = %syns-by-name{'Bq'};
    Type check failed in assignment to @res; expected Str but got Array (["Bq", "becquerel", ...) in block <unit> at <unknown file> line 1
> my Str @res = [|%syns-by-name{'Bq'}];
    [Bq becquerel becquerels]

Is this a bug, or do I misunderstand what the intent is...?

Welcome to ๐‘๐š๐ค๐ฎ๐๐จโ„ข v2020.10.
Implementing the ๐‘๐š๐ค๐ฎโ„ข programming language v6.d.
Built on MoarVM version 2020.10.
like image 760
p6steve Avatar asked Dec 26 '20 12:12

p6steve


2 Answers

TL;DR If a non-native item is mutable via assignment, it's a Scalar. List assignment does not flatten Scalar items. [1]

An inkling

Consider this code:

my %map = :a;
%map<a> = 42;
say %map<a>; # 42

The assignment works because:

say %map<a>.VAR.WHAT; # (Scalar)

"list assignment"

Consider this code:

my $scalar = 1,2;       # Useless use of constant integer 2
say $scalar;            # 1

my @list1 = 1,2;        # "list assignment", so RHS is iterated
say @list1;             # [1 2]

So that's one difference between item and list assignment.

my @list3 = [1,2];      # Again, RHS is iterated
say @list3;             # [1 2]

my @list2 = (1,2);      # Again, RHS is iterated
say @list2;             # [1 2]

my @list4 = 1,(2,3);    # Again, RHS is iterated. Second element stays as a `List`.
say @list4;             # [1 (2 3)]

my @list5 = 1,[2,3];    # Again, RHS is iterated. Second element stays as an `Array`.
say @list5;             # [1 [2 3]]

If there's just one item listed on the RHS, and it isn't a Scalar, list assignment flattens it. But in all other scenarios list assignment doesn't flatten items.

my @list6 = $[1,2];     # RHS is a `Scalar`. So it doesn't get flattened.
say @list6;             # [[1 2]]

I'm sooo confused!

Golfing the situation in the Q:

my Str @res = %( :a[42,99] )<a>;

This yields the same kind of error.

Because:

say .VAR.WHAT given :a[42,99]<a>;       # (Array)
say .VAR.WHAT given (% = :a[42,99])<a>; # (Scalar)

Footnotes

[1] When surmise yields surprise, and you turn that into learning, you realize and idealize your investment in ERNing.

like image 85
raiph Avatar answered Sep 19 '22 02:09

raiph


Note: You can't go wrong by reading @raiphs answer. However, I'll try to explain what is going on here and propose possible solutions.

In Raku, context is everything. Meaning that different contexts are triggered in different, well, contexts, implying different boxing or unboxing features of the data structure you're working with.

Let's look at this

my %syns-by-name = %(Bq => ["Bq", "becquerel", "becquerels"], C => ["C", "coulomb", "coulombs"],)

We do know we are in an "Associative" context because of the percentage marks both sides of the equal sign. We wouldn't even need it on the right hand side:

my %syns-by-name = Bq => ["Bq", "becquerel", "becquerels"], C => ["C", "coulomb", "coulombs"]

because, looking at the lhs, it's still a Associative, so we know what we have in our hands. Self same rhs code in this cotext:

my @list-of-signs = Bq => ["Bq", "becquerel", "becquerels"], C => ["C", "coulomb", "coulombs"] # [Bq => [Bq becquerel becquerels] C => [C coulomb coulombs]]

Will yield a list of pairs. So we check that what you mentioned in your OP:

Assignment to a List container (list-context) always triggers list assignment.

is tru-ish. This is a "list" container, we turn the rhs into a list just by context. There's no ambiguity of what's going on here: the rhs is a comma-separated list, the lhs is a Positional. So here you go.

The situation is slightly different in your code. It's maybe clarified a bit more if we use 2020.12

 my %syns-by-name = Bq => ["Bq", "becquerel", "becquerels"], C => ["C", "coulomb", "coulombs"]
my Str @res = %syns-by-name{'Bq'};
# Type check failed in assignment; expected Positional[Str] but got Array ($["Bq", "becquerel", ...)

Might not see the difference at first sight, so I'll highlight it here:

Type check failed in binding; expected Positional[Str] but got Array ($["Bq", "becquerel", ...)

That shows that the array is itemized, it's been boxed so that it can fit into a scalar-like thing like the value of a hash key. By default, Hash values are Scalar, with this composed into their definition:

role Associative[::TValue = Mu, ::TKey = Str(Any)] { }

So still what you mention holds: list assignment is triggered, as in "list context". However, the single item can be converted into a list only in a way: by making it the single element in a list. If you look at the error report closely, it's what it's saying: hey, you told me that you were gonna give me a list of Strs. This is not that! It's a list of (itemized) Arrays! This works:

my List @res = %syns-by-name{'Bq'};
# [(Bq becquerel becquerels)]

What we need to do is to ยซunboxยป this thing, that is, to retrieve the Array that inhabits the Scalar. Easy-peasy:

my Str @res = %syns-by-name{'Bq'}<>;
# [Bq becquerel becquerels]

The de-cont operator (which maybe should be called "unbox" or "de-Scalarize" or "de-itemize" operator) does the right thing for you. That's probably not what you want, however. You want your arrays to be arrays. That' however, has proved a bit more tricky and I am going to need another SO question to solve it.

like image 29
jjmerelo Avatar answered Sep 19 '22 02:09

jjmerelo