Home > OS >  Reuse parent symbols in child module
Reuse parent symbols in child module

Time:03-05

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

  1. 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
      
  2. Introduce Dan::Pandas.

    • Alter Dan so variant modules can use it without them importing Dan's unqualified Series symbol into their lexical scopes.

      (Because otherwise Dan's unqualified Series symbol ends up being in their lexical scopes as well as their own unqualified Series 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 the use Dan ... statement. This stops Dan's Series role being imported under that unqualified name into Dan::Pandas.

  3. User programs will now work as expected/wanted.

    • Just change the use statement in spike.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 the Dan module and the Dan::Pandas etc modules that lets them avoid the unintended role variant duplication problem shown at the start of this answer.

  • Related