I am seeking to re-use the same role/class names in a child module as in its parent. You know, like a specialization.
The aim is to be able to re-use the same script code for both the parent and child Series variants by simply changing use Dan
to use Dan::Pandas
at the top.
I am trying to stick to role rather than class compostion where I can so that the behaviours can be applied to other objects with eg. class GasBill does Series;
Here is the MRE:
me@ubuntu:~/spike# tree
.
├── lib
│ ├── Dan
│ │ └── Pandas.rakumod
│ └── Dan.rakumod
└── spike.raku
Dan.rakumod:
1 unit module Dan;
2
3 role Series is export {
4 method no { "no" }
5 }
Pandas.rakumod:
1 unit module Dan::Pandas;
2
3 use Dan;
4
5 role Series does Dan::Series is export {
6 method yo { "yo" }
7 }
spike.raku:
1 use lib './lib';
2 use Dan::Pandas;
3
4 my $s = Series.new; #version 1
5 #my $s = Dan::Pandas::Series.new; #version 2 "fqm"
6
7 say $s.no, $s.yo, $s.^name;
My version 1 says:
No appropriate parametric role variant available for 'Dan::Series':
Ambiguous call to '(Dan::Series)'; these signatures all match:
(::$?CLASS ::::?CLASS Mu $)
(::$?CLASS ::::?CLASS Mu $)
in block <unit> at spike.raku line 4
My version 2 says:
Could not find symbol '&Series' in 'Dan::Pandas'
in block <unit> at spike.raku line 5
I am trusting that raku does have a way to do this, but darned if I can work it out!
CodePudding user response:
If I'm understanding correctly, you don't need/want to use the non-specialized role in the final module (that is, you aren't using the Series
defined in Dan.rakumod
in spike.raku
– you're only using the specialized Series
defined in Pandas.rakumod
). Is that correct?
If so, the solution is simple: just don't export
the Series
from Dan.rakumod
– it's still our
scoped (the default for roles) so you can still use it in Pandas.rakumod
exactly the way you currently do (Dan::Series
). But, since it's not exported, you it won't create a name clash with the non-prefixed Series
.
CodePudding user response:
TL;DR You need a solution that avoids having two roles with the same full name.
Golf
role R {}
role R {}
R.new
displays:
No appropriate parametric role variant available for 'R':
Ambiguous call to '(R)'; these signatures all match:
(::$?CLASS ::::?CLASS Mu $)
(::$?CLASS ::::?CLASS Mu $)
(See Gut-referencing warning when composing stubbed role for a bit more discussion of the error message.)
A solution
Just
Dan
.Dan.rakumod
:unit module Dan; role Series is export { method no { "no" } }
spike.raku
:use lib '.'; use Dan; my $s = Series.new; .say for $s.?no, $s.?yo, $s.^name;
Running
spike
displays:no Nil Dan::Series
Introduce
Dan::Pandas
.Alter
Dan
so variant modules canuse
it without them importingDan
's unqualifiedSeries
symbol into their lexical scopes.(Because otherwise
Dan
's unqualifiedSeries
symbol ends up being in their lexical scopes as well as their own unqualifiedSeries
symbol, and you get the "Ambiguous call" error.)Dan
:unit module Dan; role Series is export { method no { "no" } } class Dan::Variant-Export-Dummy-Type is export(:Variant-Exports) {}
The change is the last line, a dummy declaration with a new export tag. (Introducing the new tag some cleaner way is left as an exercise for readers.)
Dan::Pandas
:unit module Dan::Pandas; use Dan :Variant-Exports; role Series is export { method yo { "yo" } }
The change is using the new
:Variant-Exports
tag with theuse Dan ...
statement. This stopsDan
'sSeries
role being imported under that unqualified name intoDan::Pandas
.
User programs will now work as expected/wanted.
Just change the
use
statement inspike.raku
:use lib '.'; use Dan::Pandas; my $s = Series.new; .say for $s.?no, $s.?yo, $s.^name;
Running with just this change for user programs:
Nil yo Dan::Pandas::Series
Note how there's no need for programs using using these modules to know about the
:Variant-Exports
tag. That's just an internal detail of theDan
module and theDan::Pandas
etc modules that lets them avoid the unintended role variant duplication problem shown at the start of this answer.