Using the default constructor
The .new constructor .new by default, maps arguments to a name for public attributes.
In your example, you pass the hash as a positional argument. You can use the syntax | to interpolate hash entries in the argument list as named arguments:
$x = Local::Class.new(|%hash);
However, note that this will cause problems if your class has an array attribute, for example has @.z :
class Local::Class { has $.x; has $.y; has @.z; } my %hash = x => 5, y => 20, z => [1, 2]; my $x = Local::Class.new(|%hash); say $x;
This is because, like all hashes, %hash places each of its values ββin an element container. Thus, the attribute will be initialized as @.z = $([1, 2]) , which leads to an array of one element, which is the original array.
One way to avoid this is to use Capture instead of Hash :
my $capture = \( x => 5, y => 20, z => [1, 2] ); my $x = Local::Class.new(|$capture); say $x;
Or use Hash , but then deconter its values ββwith <> and turn it all into a Map (which, unlike Hash , will not add element containers back) before putting it into the argument list:
my %hash = x => 5, y => 20, z => [1, 2]; my $x = Local::Class.new(|Map.new: (.key => .value<> for %hash)); say $x;
Using custom constructor
If you prefer to deal with this in the class itself rather than in the code that the class uses, you can change the constructor to your liking.
Note that the default .new calls .bless to actually allocate an object, which in turn calls .BUILD to handle attribute initialization.
Thus, the easiest way is to keep the standard .new implementation, but provide a custom .BUILD . You can map named arguments to attributes directly in your signature, so the BUILD body can actually remain empty:
class Local::Class { has $.x; has $.y; has @.z; submethod BUILD (:$!x, :$!y, :@!z) { } } my %hash = x => 5, y => 20, z => [1, 2]; my $x = Local::Class.new(|%hash); say $x;
Associating an array-in-element-container with the @ parameter automatically removes the element's container, so it does not suffer from the array-in-array problem described above.
The downside is that you have to list all the public attributes of your class in the BUILD parameter list. In addition, you still have to interpolate the hash with | in code that uses the class.
To get around both of these limitations, you can implement a custom .new like this:
class Local::Class { has $.x; has $.y; has @.z; method new (%attr) { self.bless: |Map.new: (.key => .value<> for %attr) } } my %hash = x => 5, y => 20, z => [1, 2]; my $x = Local::Class.new(%hash); say $x;