Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Destructuring assignment in object creation

As with my previous question, this is an area where I can't tell if I've encountered a bug or a hole in my understanding of Raku's semantics. Last time it turned out to be a bug, but doubt lightning will strike twice!

In general, I know that I can pass named arguments to a function either with syntax that looks a lot like creating a Pair (e.g. f :a(42)) or with syntax that looks a lot like flattening a Hash (e.g., f |%h). (see argument destructuring in the docs). Typically, these two are equivalent, even for non-Scalar parameters:

sub f(:@a) { dd @a }
my %h = a => [4, 2];
f :a([4,2]);  # OUTPUT: «Array element = [4, 2]»
f |%h;        # OUTPUT: «Array element = [4, 2]»

However, when constructing an object with the default .new constructor, these two forms seem to give different results:

class C { has @.a; }
my %h = a => [4, 2];
C.new: :a([4,2];  # OUTPUT: «C.new(a => ([[4, 2]])»
C.new: |%h;       # OUTPUT: «C.new(a => [[4, 2],])»

That is, passing :a([4,2]) results in a two-element Array, but using the argument-flattening syntax results in a one-element Array containing a two-element Array.

Is this behavior intended? If so, why? And is there syntax I can use to pass |%h in and get the two-element Array bound to an @-sigiled attribute? (I know using an $-sigiled attribute works, but I prefer the semantics of the @).

like image 816
codesections Avatar asked Feb 03 '23 13:02

codesections


1 Answers

Is this behavior intended?

Yes. Parameter binding uses binding semantics, while attribute initialization uses assignment semantics. Assignment into an array respects Scalar containers, and the values of a Hash are Scalar containers.

If so, why?

The intuition is:

  • When calling a function, we're not going to be doing anything until it returns, so we can effectively lend the very same objects we pass to it while it executes. Thus binding is a sensible default (however, one can use is copy on a parameter to get assignment semantics).
  • When creating a new object, it is likely going to live well beyond the constructor call. Thus copying - that is, assignment - semantics are a sensible default.

And is there syntax I can use to pass |%h in and get the two-element Array bound to an @-sigiled attribute?

Coerce it into a Map:

class C { has @.a; }
my %h = a => [4, 2];
say C.new: |%h.Map;

Or start out with a Map in the first place:

class C { has @.a; }
my %h is Map = a => [4, 2];
say C.new: |%h;
like image 79
Jonathan Worthington Avatar answered Feb 15 '23 17:02

Jonathan Worthington