Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Problem when using role between two module

Tags:

raku

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 ?

like image 547
araraloren Avatar asked Sep 09 '18 15:09

araraloren


3 Answers

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.

like image 34
raiph Avatar answered Nov 09 '22 23:11

raiph


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...

like image 83
Christoph Avatar answered Nov 09 '22 23:11

Christoph


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.

like image 36
jjmerelo Avatar answered Nov 10 '22 00:11

jjmerelo