Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[EXPERIMENTAL] Implement support for role's COMPOSE submethod in Raku v6.e #5312

Open
wants to merge 3 commits into
base: main
Choose a base branch
from

Conversation

vrurg
Copy link
Member

@vrurg vrurg commented Jul 13, 2023

The submethod is implementing the funcionality many expect from a phaser of the same name. But the submethod can be considered more fit to the task as it's kind of getting into the line with construction submethods BUILD and TWEAK with the difference that it is role-specific and it is about type-composition time, not object-composition.

The submethod is invoked on role concretization before it gets applied to a class.

This is currently just an experiment. A couple of small touches would be needed to have it merge-ready.

The submethod is implementing the funcionality many expect from a phaser
of the same name. But the submethod can be considered more fit to the
task as it's kind of getting into the line with construction submethods
`BUILD` and `TWEAK` with the difference that it is role-specific and it
is about type-composition time, not object-composition.

The submethod is invoked on role concretization before it gets applied
to a class.
@vrurg
Copy link
Member Author

vrurg commented Jul 13, 2023

This is an experimental implementation for my proposal in Raku/problem-solving#376. With the following snippet the submethods would be invoked for R0, R1, R2 in this order:

use v6.e.PREVIEW;

role R0 { 
    submethod COMPOSE {
        note "COMPOSE on ", self.^name, " of ", self.HOW.^name, " for ", ::?CLASS.^name;
    }
}
role R1 does R0 { 
    submethod COMPOSE {
        note "COMPOSE on ", self.^name;
    }
}
role R2 does R0 { 
    submethod COMPOSE() {
        note "COMPOSE on ", self.^name, " // ", self.HOW.^name, " // ", self ~~ R2;
    }
}

class C does R1 does R2 { }

As a matter of fact, it would better do as a sub, but we don't use
global subs in the Metamodel. Therefore, for now, I move it into
Perl6::Metamodel::Configuration. This is a temporary experimental
solution as it would make more sense to have a class dedicated to
utility methods of similar kind.
Make EnumHOW aware of role concretizations. The rest is done by
RoleToClassApplier.
@vrurg vrurg changed the title Implement support for role's COMPOSE submethod in Raku v6.e [EXPERIMENTAL] Implement support for role's COMPOSE submethod in Raku v6.e Jul 13, 2023
@Xliff
Copy link
Contributor

Xliff commented Jul 13, 2023

Will proto methods have their candidates available at compose time?

I will grab it and check, but if you happen to test this and find out, please let me know.

Here is the code I am using for testing:

use Method::Also; 

class A { method a is also<b> { 1 }; }; 
my $a = A.new;
$a.b.say; 
role R { 
  proto method c_d (|) is also<d-e> { * }; 
  multi method c_d { 43 }; 
  multi method c_d (Str $a) { "a" }; 
}; 
$a does R; 
$a does AliasesFromRole[R]; 
$a.c_d.say;
$a.d-e.say;

Please note, I am working from my copy of Method-Also at https://github.com/Xliff/Method-Also, branch role-body.

@Xliff
Copy link
Contributor

Xliff commented Jul 13, 2023

So... after a quick compile and a small change, here is my revised test code:

use v6.e.PREVIEW; 

use Method::Also; 

class A { 
  method a is also<b> { 1 }; 
}; 

my $a = A.new;

$a.b.say; 

role R { 
  also does AliasedMethods; 

  proto method c_d (|) is also<d-e> { * }; 

  multi method c_d { 43 }; 

  multi method c_d (Str $a) { "a" }; 
}; 

$a.c_d.say; 

$a.d-e.say;

Results:

()
Submethod!
{}
(R)
1
No such method 'c_d' for invocant of type 'A'
  in block <unit> at -e line 1```

So it appears that the COMPOSE submethod isn't late enough for the existing aliasing mechanism.

Could you give a look at the existing code in Method::Also and create an issue with your recommended changes?

Thanks.

@vrurg
Copy link
Member Author

vrurg commented Jul 13, 2023

@Xliff unfortunately, I wouldn't be able to help with Method::Also due to lack of time. Having half a day for this experiment was kind of a miracle. But I have a few notes.

First of all, speaking of the COMPOSE itself, there is no reason for it to fire after role application is complete. In my view best time for it is when the target class has done all the primary preparations and is ready to accept objects from roles. Considering that composing multis is a process that requires data from roles, this stage must happen after the application. With this in mind, best time for COMPOSE is after all class attributes and their accessors are installed. At this point multis are not incorporated yet.

Next, you operate on the class. This is a mistake. Role HOWs are consumers of Metamodel::MultiMethodContainer. I'm not sure what approach would work the best, but in my opinion it must include the step where you operate on multi method candidates. Either by overriding add_multi_method to intervene at the early stages; or by iterating over multi_methods_to_incorporate at the later stages.

@Xliff
Copy link
Contributor

Xliff commented Jul 13, 2023

Can a role override .add_multi_method ?

EDITED: Answer - No.

@vrurg
Copy link
Member Author

vrurg commented Jul 13, 2023

Continuing our IRC conversation, a role – can't, unless it is a role mixed into HOW. See below:

role AliasHOW {
    has %!aliases;

    method register-alias(\obj, $from, $to) {
        %!aliases{$from} = $to;
    }

    method add_multi_method(Mu \obj, $name, \code-obj) is raw {
        note "ADDING MULTI ", $name, " ", code-obj.raku;
        with %!aliases{$name} {
            note "  $name -> $_";
            callwith(obj, $_, code-obj);
        }
        callsame
    }
}

multi sub trait_mod:<is>(Method:D \m, Str :$alias!) {
    note "Alias $alias for ", m.raku;
    m.package.HOW does AliasHOW unless m.package ~~ AliasHOW;

    m.package.^register-alias(m.name, $alias);
}

role R {
    proto method foo(|) is alias<bar> {*}
    multi method foo {
    }
}

class C does R { }

C.bar;

And don't forget that this snippet only makes sense for the trait applied to a proto.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

None yet

2 participants