Assignment to a List Container Confusion

人走茶凉 提交于 2021-01-26 03:51:57

问题


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.

回答1:


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.




回答2:


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.



来源:https://stackoverflow.com/questions/65456629/assignment-to-a-list-container-confusion

标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!