On Wed Jan 20 10:44:39 2016, elizabeth wrote:
> > On 19 Jan 2016, at 15:05, Peter Pentchev (via RT) <perl6-bugs-
> > follo...@perl.org> wrote:
> >
> > # New Ticket Created by  Peter Pentchev
> > # Please include the string:  [perl #127315]
> > # in the subject line of all future correspondence about this issue.
> > # <URL: https://rt.perl.org/Ticket/Display.html?id=127315 >
> >
> >
> > Hi,
> >
> > First of all, thanks a lot for everything you've all done so far and
> > keep doing!
> >
> >
> > Now, I'm the author of the Serialize::Naive module (and, yeah, I
> > guess I should rename the Serialize-Naive distribution to use "::"
> > and not "-"), and it has a documented inability to serialize and
> > deserialize classes with attributes declared with the @. or %.
> > twigils.  Of course, this can be worked around by "has Array[Str]
> > $.foo", but still it feels somewhat incomplete to me :)
> >
> > I've attached a sample file that demonstrates the several ways I've
> > tried to do that; I'm somewhat baffled by the "expected Str but got
> > Array[Str]" error - it looks like something tries to interpret the
> > hash element's value as a sequence containing a single array, while
> > it should interpret it as, well, an array, and get the values from
> > it... I think.
> >
> > A minimum viable problem in case you don't want to slog through all
> > the diagnostics and conditionals:
> >
> > perl6 -e 'use v6; use strict; class Foo { has Str @.bar; }; my $f1 =
> > Foo.new(bar => <a b c>); say $f1.perl;  my %data = bar => <a b c>; my
> > $f2 = Foo.new(|%data); say $f2.perl;'
> >
> > Foo.new(bar => Array[Str].new("a", "b", "c"))
> > Type check failed in assignment to @!bar; expected Str but got List
> >  in block <unit> at -e line 1
> >
> > Both this one-liner and the attached file fail in the same way in
> > 2015.12 and in:
> > This is Rakudo version 2015.12-213-g770d109 built on MoarVM version
> > 2015.12-29-g8079ca5
> > implementing Perl 6.c.
> >
> > Thanks in advance for any advice or assistance, and keep up the great
> > work!<class-assign.p6>
> 
> Looks like this is really the root cause of the issue:
> 
> $ 6 'my %h = a => <a b c>; dd %h'
> Hash %h = {:a($("a", "b", "c"))}
> 
> I think this should actually say:
> Hash %h = {:a(("a", "b", "c"))}
> 
> Somehow the list is getting itemized when assigning to the hash.
> 
The behavior here is correct. Hashes and arrays - natives arrays aside - store 
their values in scalar containers. This is why both assignment to arrays/hashes 
and also autovivification works - both of which are rather important! :-) It's 
also why:

my @a = flat @b, @c;

Will give you an @a with the things in @b, followed by the things in @c, but 
not magically descend into things that just happen to be Iterable. Again, this 
is generally desirable, otherwise it would be very hard to predict what the 
above code might do.

The confusion typically arises when this behavior is combined with places that 
the one-arg rule applies, such as in assignment to arrays:

my @a = 1,2; say @a.elems;    # 2
my @a = $(1,2); say @a.elems; # 1

Again, both are what you'd expect. Taking the above behaviors together, however:

my %h = a => (1, 2, 3);  # hash values go in Scalars
my @a = %h<a>;           # one-arg rule sees Scalar
say @a.elems;            # and so there's 1 elem here

There are a number of ways around that. One is at the point of assignment:

my %h = a => (1, 2, 3);
my @a = @(%h<a>);        # treat it as a list
say @a.elems;

That second line is often instead written as:

my @a = %h<a>.list;

The alternative is to bind things into the Hash, which is more useful if 
constructing an object:

my %h;
%h<a> := (1,2,3);   # no Scalar introduced
my @a = %h<a>;      # so one-arg rule iterates the items
say @a.elems;       # giving 3 elems

Of course, since there's no Scalar container in the hash value for the key a, 
assignment to the hash key isn't possible:

%h<a> = (4,5,6);    # error

Which is generally not what people expect of a hash, which is why the hash 
composer and list assignment to a hash default to assignment semantics, not 
binding semantics.

So in answer to the original question, binding instead of assigning into the 
hash used to construct the object is likely the best solution.

class C {
    has @.a;
}
my %h;
%h<a> := 1,2,3;
say C.new(|%h).a.elems; # 3

I'll reject the ticket as not a bug, since things are behaving as designed. 
Somebody may wish to pop the above explanation into the FAQ, since I think 
it'll probably come up a few more times in the future. (And no, I don't expect 
any design change here, since this behavior falls out of two generally 
desirable and quite fundamental things. And we've already been down the path of 
plenty more magical/DWIM-y list/fattening designs, and they caused more 
confusion than they were worth.)

Hope this helps,

/jnthn

Reply via email to