Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to access mixed-in components when the original variable has a default coercion?

Tags:

mixins

raku

For instance, in this case:

my @list = (2,) but "bar";
put @list.Str «2␤»

There does not seem to be a way to access the "bar" component. Am I missing something? Same will happen, for instance, with Set

my @list = (2,3) but Set(4,5);
put @list.Set; # OUTPUT: «3 2␤»
like image 490
jjmerelo Avatar asked Dec 09 '22 23:12

jjmerelo


2 Answers

Assignment is a copying operation, so:

my @a = something;

Creates an Array in @a, iterates something, and stores each element in @a. If binding is used instead:

my @list := (2,) but "bar";
put @list.Str;

Then the list with the mixin is bound to the symbol @list, and the output is:

bar
like image 123
Jonathan Worthington Avatar answered Jan 20 '23 14:01

Jonathan Worthington


TL;DR I think I'm now closer to understanding what you meant. I share various solutions based on that understanding at the start of this answer. (The rest of this answer is perhaps now mostly moot, but I've left it in because I think it still has some value, especially the apparent bug which still baffles me.)

Some solutions to what I now think you want

role foo { method Str { 42 } }
...
say @bar.Str; # 42

where the ... is suitably filled in, eg one of:

  • class List2 is List does foo {}
    my @bar is List2;
    

    What this does, and when:

    • Creates a new container type List2 composing List with role foo.

    • Permanently "binds" the variable @bar to a new instance of the new container type.

    • All of this happens at compile-time.

  • my @bar is List;
    @bar does foo;
    

    What this does, and when:

    • Permanently "binds" the variable @bar to a new instance of List at compile-time.

    • Permanently mixes foo into the same List instance at run-time.

  • my @bar := (1,2) but foo
    

    What this does, and when:

    • Constructs a new List instance at compile-time from the literal ((1,2)).

    • Constructs a new instance at run-time that's a clone of the List constructed from the list literal but with the foo role mixed in.

    • Temporarily "binds" the variable @bar to the new List / foo object ((1,2) but foo).

My original answer

The code you shared, and its behaviour, is mostly basic ordinary behaviour. jnthn's answer explains these basic aspects. This answer provides a bit more detail and notes one part that surprised me and might be a bug.

Is there a way to access mixed-in components when the original variable has a default coercion?

I think I see something unexpected going on that might be what you're talking about. But I wouldn't describe it as "default coercion" given that both "default" and "coercion" already have specific meanings in Raku, and it's not to do with those. Modulo the strange behaviour it's just Raku's ordinary semantics for assigning a plural value to a plural variable.

For instance, in this case:

my @list = (2,) but "bar";

jnthn's answer explains why that's not going to work. In a nutshell the mixin is on the plural value, not on each of the elements within that plural value, so it won't get copied as part of the assignment. Your code is "broken" before you start.

You could reasonably write something like this and expect it to work:

my @list = (2,) Zbut "bar";
put @list.Str; # «bar␤»

The Z in the first line distributes the but over the singular elements in the plural List value using zip semantics. There's only one element -- 2 -- but it also works if there are more:

my @list = 2,3 Zbut "bar", "foo";
put @list.Str; # «bar foo␤»

It's useful to be clear about what's happening in that second line.

The .Str method is being called on a plural value (an Array that gets implicitly created and bound to @list as part of declaring my @list).

As with most programming languages, each routine in Raku gets to decide what it's going to do based primarily on its arguments. And like most PLs with methods, methods can do perhaps surprisingly different things depending on their invocant.

In Raku the .Str method, when called with a plural value/variable invocant distributes a .Str to each element in that plural invocant. (And if any of its elements is itself another plural that call will recursively do the same to its elements.) When all is done, the series of resulting individual strings are concatenated with a space between each.

Same will happen, for instance, with Set

As before, you would again need to distribute the mixin to the individual elements in the first line to have any chance of things working out:

my @list = (2,3) Zbut Set(4,5);

But now the second line still won't do what you're expecting:

put @list.Set; # OUTPUT: «3 2␤»

The issue is that this time you're dealing with .Set.

Calling .Set on a plural is not like calling .Str on a plural. It has completely different semantics. What it does is construct a set from the plural value's collection of elements. Thus:

say (1,2,3).Set; # Set(1 2 3)

Hopefully this has all made sense thus far.

Here's where we arrive at what has surprised me. This seems to work:

my @list = 2 but Set(4,5);
put @list[0].^name;       # OUTPUT: «Int+{<anon|1>}␤»
put @list[0].Set;         # OUTPUT: «4 5␤»
put @list[0].Set.^name;   # OUTPUT: «Set␤»

But if we change the first line, things don't work as I would expect:

my @list = (2,3) Zbut Set(4,5);
put @list[0].^name;       # OUTPUT: «Int+{<anon|1>}␤»
put @list[0].Set;         # OUTPUT: «2␤»
put @list[0].Set.^name;   # OUTPUT: «Set␤»

That's baffling me. I will sleep on it. But right now that seems like it might be a bug.

like image 29
raiph Avatar answered Jan 20 '23 14:01

raiph