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

Roles, submethods, constructors, destructors. #103

Closed
vrurg opened this issue Sep 10, 2019 · 5 comments
Closed

Roles, submethods, constructors, destructors. #103

vrurg opened this issue Sep 10, 2019 · 5 comments
Assignees
Labels
6.e Related to the next 6.e language release language Changes to the Raku Programming Language

Comments

@vrurg
Copy link
Contributor

vrurg commented Sep 10, 2019

In this ticket I'd like to gather scattered pieces of one big problem on role composition and submethod handling related to this. Related topics are rakudo/rakudo#2241, rakudo/rakudo#2250, rakudo/rakudo#2677, rakudo/rakudo#2678.

In general, the problem is about:

  1. How submethods in roles are composed into the consuming classes and if they should be composed at all?
  2. How we call submethods.
  3. How constructors/destructors are treated and how to use those defined in roles.
@vrurg vrurg added the language Changes to the Raku Programming Language label Sep 10, 2019
@vrurg
Copy link
Contributor Author

vrurg commented Sep 10, 2019

I'd like to propose a set of rules on treating roles and their composition in to classes.

A submethod belongs to where it is defined.

Contrary to methods, submethods from roles must not be copied over to the consuming class. The following:

role R {
    submethod foo { }
}
class C does R {
}
C.new.foo

must be a method not found error. The right call to foo would be C.new.R::foo.

Calling a submethod results in a single call.

In rakudo/rakudo#2677 it is proposed by @jnthn and backed by @TimToady that if a submethod is declared on both class and and role then calling it on a class instance would result in both submethods being called:

role R { submethod foo { say "R" } }
class C does R { submethod foo { say "C" } }
C.new.foo; # R\nC\n

My point of view is that there is no reason to break the existing semantics of one call - one (sub)method. For cases where one needs a batch-call there're already .+ and .* operators. But I'll get back to them in the following section.

Another point is what implicit batch-call must return? How would it handle flattening returns?

Also, what would be the way for a user to call submethods individually when it is necessary? Wouldn't implicit batch-call over-complicate this rather simple task?

Batch-call of submethods must be possible in reverse MRO.

This is quite natural semantics for submethods following how constructors/destructors are being used. But it shouldn't be the only possible way as implicit batch-calling implies. My point of view is that in addition to the mention above .+ and .* we could provide .- which does the same as .+ but in reverse MRO. With the operator it's easier to provide DWIM semantics for a user.

Unfortunately, I cannot find appropriate analog for reverse version of .*.

Attribute flattening

Consider the example:

role R1 { has $.a }
role R2 does R1 { }
role R3 does R1 { }
class C does R2 does R3 { }

This code currently results in Attribute '$!a' conflicts in role composition error. This happens because R1 is flattened into R2 and R3 causing the conflict when both are flattened into C.

What must happen is $.a must only be flattened at C composition time directly from R1.

rakudo/rakudo#2694

@vrurg
Copy link
Contributor Author

vrurg commented Sep 10, 2019

MRO and mro method

By default mro method doesn't report back roles. But if called with :roles named parameter it will include them into the result. I call this RMRO or Rolified MRO. Possible output can be found in this comment. Method mro_unhidden in turn respects the is hidden trait and only returns non-hidden classes and roles with :roles.

Constructors/destructors

Basically, they follow common submethod semantics. The interesting point here though is classes inheritance when child consumes same role one of its parents already does. The answer to this is again lies in the mentioned above comment. The sample RMRO there looks like:

C1 R1 R0 C0 R3 R1 R2 R0

Reversed it provides the order of calling constructors/destructors meaning that if R0 and R1 define any they'd be executed twice per each consuming class.

Cross-version compatibility

Roles created with 6.e language version are not to be consumed by 6.d- classes. Though it should in general be possible for a 6.e class to consume 6.d- role.

NOTE Metamodel::ClassHOW already does Metamodel::LanguageRevision role. ParametricRoleHOW is a subject to change yet.

@vrurg
Copy link
Contributor Author

vrurg commented Sep 10, 2019

This is a draft I propose for consideration. It may also be extended to cover rakudo/rakudo#2663.

vrurg added a commit to vrurg/rakudo that referenced this issue Sep 28, 2019
Will be useful for submethod traversing when Raku/problem-solving#103
is implemented.

Roles are included into `mro` and `mro_unhidden` output when called with
`:roles` named parameter. Because roles are integral part of their
consuming classes, they always follow their class in MRO list no matter
what was the order of declaration. I.e.:

```
class C1 does R1 is C1 does R2 { };
say C1.^mro(:roles).map: *.^name; # (C1 R2 R1 C2 Any Mu)
```

`mro_unhidden` won't include roles of a hidden class into the list.
@vrurg
Copy link
Contributor Author

vrurg commented Oct 4, 2019

More on batch-call operators

It was proposed more as a joke but suddenly it might make sense to introduce .?+ and .?- operators. It would make a complete matrix of method call operators:

  • . for simple calls, fails if no method.
  • .? ignores missing method
  • .+ batch-calls methods on MRO, fails
  • .- batch-calls method on MRO in reverse order, fails
  • .?+ and .?- do the same as .+ and .- but instead of failing they put Failure wrapped around X::Method::NotFound where methods are not found on a type object.

The above set is sufficient. Questions arise though:

  • .* becomes redundant. Deprecate?

  • It makes sense executing submethods on all MRO type objects, including roles. What about just methods? Do we also find them on roles and call? But this would break backward compatibility with the current behavior of .+. Besides, submethods, contrary to methods, are not copied over from roles to classes. Thus they are not likely to suffer from double-calls caused by batch-processing.

    So, I'd rather proclaim that for classes the above batch-call ops would pick up any of methods or submethods; whereas only submethods will be taken from roles.

vrurg added a commit to vrurg/rakudo that referenced this issue Oct 5, 2019
Will be useful for submethod traversing when Raku/problem-solving#103
is implemented.

Roles are included into `mro` and `mro_unhidden` output when called with
`:roles` named parameter. Because roles are integral part of their
consuming classes, they always follow their class in MRO list no matter
what was the order of declaration. I.e.:

```
class C1 does R1 is C1 does R2 { };
say C1.^mro(:roles).map: *.^name; # (C1 R2 R1 C2 Any Mu)
```

`mro_unhidden` won't include roles of a hidden class into the list.
vrurg added a commit to vrurg/rakudo that referenced this issue Nov 18, 2019
Will be useful for submethod traversing when Raku/problem-solving#103
is implemented.

Roles are included into `mro` and `mro_unhidden` output when called with
`:roles` named parameter. Because roles are integral part of their
consuming classes, they always follow their class in MRO list no matter
what was the order of declaration. I.e.:

```
class C1 does R1 is C1 does R2 { };
say C1.^mro(:roles).map: *.^name; # (C1 R2 R1 C2 Any Mu)
```

`mro_unhidden` won't include roles of a hidden class into the list.
@vrurg vrurg added the 6.e Related to the next 6.e language release label Nov 27, 2019
@vrurg vrurg added this to In Development in v6.e Release Nov 27, 2019
vrurg added a commit to vrurg/rakudo that referenced this issue Dec 2, 2019
Will be useful for submethod traversing when Raku/problem-solving#103
is implemented.

Roles are included into `mro` and `mro_unhidden` output when called with
`:roles` named parameter. Because roles are integral part of their
consuming classes, they always follow their class in MRO list no matter
what was the order of declaration. I.e.:

```
class C1 does R1 is C1 does R2 { };
say C1.^mro(:roles).map: *.^name; # (C1 R2 R1 C2 Any Mu)
```

`mro_unhidden` won't include roles of a hidden class into the list.
@vrurg
Copy link
Contributor Author

vrurg commented Dec 10, 2019

Closing with merge of rakudo/rakudo#3199 and Raku/roast#581

@vrurg vrurg closed this as completed Dec 10, 2019
v6.e Release automation moved this from In Development to Done Dec 10, 2019
vrurg added a commit to vrurg/rakudo that referenced this issue Dec 11, 2019
Will be useful for submethod traversing when Raku/problem-solving#103
is implemented.

Roles are included into `mro` and `mro_unhidden` output when called with
`:roles` named parameter. Because roles are integral part of their
consuming classes, they always follow their class in MRO list no matter
what was the order of declaration. I.e.:

```
class C1 does R1 is C1 does R2 { };
say C1.^mro(:roles).map: *.^name; # (C1 R2 R1 C2 Any Mu)
```

`mro_unhidden` won't include roles of a hidden class into the list.
vrurg added a commit to vrurg/rakudo that referenced this issue Dec 12, 2019
Will be useful for submethod traversing when Raku/problem-solving#103
is implemented.

Roles are included into `mro` and `mro_unhidden` output when called with
`:roles` named parameter. Because roles are integral part of their
consuming classes, they always follow their class in MRO list no matter
what was the order of declaration. I.e.:

```
class C1 does R1 is C1 does R2 { };
say C1.^mro(:roles).map: *.^name; # (C1 R2 R1 C2 Any Mu)
```

`mro_unhidden` won't include roles of a hidden class into the list.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
6.e Related to the next 6.e language release language Changes to the Raku Programming Language
Projects
v6.e Release
  
Done
Development

No branches or pull requests

2 participants