6

I am making a module that has multi module file, and faced this problem when using role in different module.

For example we have two module Foo and Bar, and there is a role in each module.

module Foo { 
    role foo is export {

    }  
}

module Bar { 
    import Foo; 

    role bar is export does foo {

    }
} 

import Foo;
import Bar;

sub f(foo \k) { }

f(bar.new);

I thought the code is fine, but rakudo said it think bar is not a foo and reject to compile.

What's wrong here ?

araraloren
  • 185
  • 7

3 Answers3

5

Update in 2022 I just checked and in v2022.02 this code now works as expected.


The symbol foo after import isn't =:= to Foo::foo and doesn't accept the latter in a smart match. That seems like a bug to me and is presumably related to what's going on:

module Foo {
  role foo is export { }
  .say for foo.^name, Foo::foo.^name,
           Foo.WHICH, foo.WHICH, Foo::foo.WHICH,
           foo.isa(Foo::foo),
           Foo::foo.isa(foo),
           foo.does(Foo::foo),
           Foo::foo.does(foo),
           foo ~~ Foo::foo,
           Foo::foo ~~ foo,
}
import Foo;
.say for foo.^name, Foo::foo.^name,
         Foo.WHICH, foo.WHICH, Foo::foo.WHICH,
         foo.isa(Foo::foo),
         Foo::foo.isa(foo),
         foo.does(Foo::foo),
         Foo::foo.does(foo),
         foo ~~ Foo::foo,
         Foo::foo ~~ foo,

Foo::foo
Foo::foo
Foo|U64545472
Foo::foo|U64545856
Foo::foo|U64545856
False
False
True
True
True
True
Foo::foo
Foo::foo
Foo|U64545472         <^-- all good so far
Foo::foo|U64545616    <--- uhoh
Foo::foo|U64545856
False
False
True
True
True
False                 <-- presumably a consequence of uhoh

I'll file a bug in the next couple days if no one beats me to it and no one shows why it's not a bug.

raiph
  • 31,607
  • 3
  • 62
  • 111
5

This looks like a bug to me. Some further investigation:

module Foo {
    role Bar is export {}
}

module Quux {
    import Foo;
    constant Barr = Bar;
    role Baz does Bar is export {}
    role Bazz does Foo::Bar is export {}
}

import Foo;
import Quux;

# these are all the same: 
say Foo::EXPORT::ALL::Bar.WHICH;
say Quux::Barr.WHICH;
say Bar.WHICH;

# but different from our original type object!?!
say Foo::Bar.WHICH;

# now it gets weird:
say Baz ~~ Bar;           # True
say Baz.new ~~ Bar;       # False
say Baz ~~ Foo::Bar;      # False
say Baz.new ~~ Foo::Bar;  # True

# however, these all 'work':
say Bazz ~~ Bar;          # True
say Bazz.new ~~ Bar;      # True
say Bazz ~~ Foo::Bar;     # True
say Bazz.new ~~ Foo::Bar; # True

For now, it seems best to only derive new roles from the fully qualified public version of another module's role instead of an exported one: Exporting seems to create a new type object that interacts strangely with smart matching/type checking...

Christoph
  • 164,997
  • 36
  • 182
  • 240
3

Let me see if I can channel @raiph and answer this question in the best way possible. There are several errors here.

Using import

The main use case for import is exactly the one you do here; it's described in the documentation. However, it's a better practice to put modules in different files (with the same name as the module) and go for use instead. use which imports and includes everything into the namespace. use loads (via need) and then imports the module exactly once.

Save curly braces using unit

unit is basically syntactic sugar to save braces. So the first two files will become:

Foo.pm6

unit module Foo;

  role foo is export {
} 

Bar.pm6

unit module Bar;

use Foo; 

role bar is export does foo {}

The use of Foo imports the foo symbol directly, and it can be used as a role.

What is a role

Last but not least, a role mixed in does is literally a "does" declaration. It's not an "is-a" declaration. So bar can do foo, but it's not foo. Which makes the last file something like this:

use-foo-bar.p6:

use Foo;
use Bar;
sub f(bar \k) { }
f(bar.new);

Note that I have used bar \k instead of foo \k. If I didn't, the error would be: Type check failed in binding to parameter 'k'; expected Foo::foo but got Bar::bar (Bar::bar.new)

Back to the original post

What was wrong was simply the sub declaration, which should have been:

sub f(bar \k) { }

Due to what is explained in the last section above. However, I needed to examine a bit the rest of the program to find this out.

jjmerelo
  • 22,578
  • 8
  • 40
  • 86
  • *"Using import: It is so discouraged..."* : I wouldn't use the word *discouraged*, maybe a better word would be *recommended*. Since it has at least one use case: namely exactly the case of the question, where modules are defined in the same file. In this case, you would get an error if you used `use Foo`, whereas `import Foo` is now the correct approach.. – Håkon Hægland Sep 10 '18 at 13:15
  • Also, I think it is better to declare `f` to take a `Foo::foo`, i,e, `sub f(Foo::foo \k) { }`. In general `Bar::bar` and `Foo::foo` might not be the same type. But I am still confused why we have to write `Foo::foo` instead of `foo`.. – Håkon Hægland Sep 10 '18 at 13:31
  • @HåkonHægland thanks for the suggestion. I'll edit my answer to take that into account. With respect to the second, theoretically import should import it into the namespace. So, as above, it might be a bug... – jjmerelo Sep 10 '18 at 14:04
  • 1
    Hi JJ. I reckon you're channeling [this raiph](https://stackoverflow.com/a/51648069/1077672) not me. ;) – raiph Sep 10 '18 at 14:20
  • 1
    @jjmerelo, I would contradict to using `bar` instead of `foo` in your example. I mean, while it could be a correct thing from Perl6 implementation perspective, it is totally wrong from code design point of view. Say I wrote a role `Foo` for public use. And export a sub which works with objects doing the role. After that it is out of my hands if somebody creates role `Bar` doing `Foo`. From my point of view the sub could still do its job with `Bar`! And `( Foo $obj )` signature is a simple and elegant way of declaring it whereas `( $obj where * ~~ Foo )` is valid but not elegant. – Vadim Belman Sep 11 '18 at 18:02
  • @VadimBelman you are probably right, but I was just doing a minimal change to make the code not error. There are several design issues which I have pointed out, but the gist of it is understanding that a Role is not a superclass. – jjmerelo Sep 12 '18 at 10:15