What does the keyword 'unit' before a package name do? - raku

In the following code;
unit module Fancy::Calculator;
what does 'unit' actually do? I know that the scope for the definition of the module becomes the file its declared in - as opposed to;
module Fancy::Calculator {
# module definition statements here
}
where the scope is obviously defined by the curlies but I can't see anything in the documentation that definitively states that that is all that it does and I'd be a little surprised if that's all that it did. Secondarily, after making such a declaration, can one declare unit class Whatever (class, module, whatever) half way down and call an end to the previous scope definition?

From a commenter (thanks Brad), it appears that is all it does. As for starting a second Module within the same file - you can't use unit module again - that produces;
===SORRY!=== Error while compiling /home/user/Fancy/Calculator.pm6
Too late for unit-scoped module definition;
Please use the block form.
...but as the message says, you can use the block form but whatever you declare is within the unit module namespace - Fancy::Calculator in this case. So these;
unit module Fancy::Calculator;
# The following available to the module user as Fancy::Calculator::Adder
class Adder {
method add { "Hi... I am a method who can add" }
}
# Starting definition of new module but its within Fancy::Calculator namespace
module Minus {
# Following available to the module user as Fancy::Calculator::Minus::Subber
class Subber {
method subtract { "Hi... I am a method who can subtract" }
}
# unless you add "is export" in which case its available by its short name
class Multiplyer is export {
method times { "Hi... I am a method who can multiply" }
}
sub divide() is export { "Hi... I am a sub who can divide" }
}
are accessed like this;
# In main
use Fancy::Calculator;
my $fca = Fancy::Calculator::Adder.new;
say $fca.add; # Hi... I am a method who can add
my $fcms = Fancy::Calculator::Minus::Subber.new;
say $fcms.subtract; # Hi... I am a method who can subtract
my $mul = Multiplyer.new;
say $mul.times; # Hi... I am a sub who can multiply
say divide(); # Hi... I am a sub who can divide

Related

Testing private methods in Raku

Is there a way to test private methods in Raku?
I understand that one should ideally define their tests targeting the public methods, but is there a way to do it "the wrong way"? :)
I initially thought about defining a subclass for the Testing that inherited from the class I wanted to test and do the tests there, but it seems that private methods are not inherited.
Then I saw the 'trusts' routine, but I wouldn't want to reference a Testing class on any of the classes of the code.
Is there something like changing the 'private' property of a method via introspection?
What would be the best way to call/test a private method?
This can be done using introspection.
Consider this is the class you want to test:
class SomeClass {
has Int $!attribute;
method set-value(Int $value) returns Nil {
$!attribute = $value;
return;
}
method get-value returns Int {
return $!attribute;
}
# Private method
method !increase-value-by(Int $extra) returns Nil {
$!attribute += $extra;
return;
}
}
You may create a test like this:
use Test;
use SomeClass;
plan 3;
my SomeClass $some-class = SomeClass.new;
my Method:D $increase-value = $some-class.^find_private_method: 'increase-value-by';
$some-class.set-value: 1;
$increase-value($some-class, 4);
is $some-class.get-value, 5, '1+4 = 5';
$increase-value($some-class, 5);
is $some-class.get-value, 10, '5+5 = 10';
my SomeClass $a-new-class = SomeClass.new;
$a-new-class.set-value: 0;
$increase-value($a-new-class, -1);
is $a-new-class.get-value, -1, '0+(-1) = -1; The method can be used on a new class';
done-testing;
You first create an instance of the class and the use ^find_private_method to get its private Method. Then you can call that Method by passing an instance of a class as the first parameter.
There's a more complete explanation on this answer:
How do you access private methods or attributes from outside the type they belong to?
A fresh cup of tea and #Julio's and #JJ's answers inspired the following:
class SomeClass { method !private ($foo) { say $foo } }
use MONKEY-TYPING; augment class SomeClass { trusts GLOBAL }
my SomeClass $some-class = SomeClass.new;
$some-class!SomeClass::private(42); # 42
My solution tweaks the class using monkey typing. Monkey typing is a generally dodgy thing to do (hence the LOUD pragma). But it seems tailor made for a case just like this. Augment the class with a trusts GLOBAL and Bob's your Uncle.
Raku requires the SomeClass:: qualification for this to work. (Perhaps when RakuAST macros arrive there'll be a tidy way to get around that.) My inclination is to think that having to write a class qualification is OK, and the above solution is much better than the following, but YMMV...
Perhaps, instead:
use MONKEY-TYPING;
augment class SomeClass {
multi method FALLBACK ($name where .starts-with('!!!'), |args) {
.(self, |args) with $?CLASS.^find_private_method: $name.substr: 3
}
}
and then:
$some-class.'!!!private'(42); # 42
I've used:
A multi for the FALLBACK, and have required that the method name string starts with !!!;
A regular method call (. not !);
Calling the method by a string version of its name.
The multi and !!! is in case the class being tested already has one or more FALLBACK methods declared.
A convention of prepending !!! seems more or less guaranteed to ensure that the testing code will never interfere with how the class is supposed to work. (In particular, if there were some call to a private method that didn't exist, and there was existing FALLBACK handling, it would handle that case without this monkey FALLBACK getting involved.)
It should also alert anyone reading the test code that something odd is going on, in the incredibly unlikely case that something weird did start happening, either because I'm missing something that I just can't see, or because some FALLBACK code within a class just so happened to use the same convention.
Besides using introspection, you can try and use a external helper role to access all private methods and call them directly. For instance:
role Privateer {
method test-private-method ( $method-name, |c ) {
self!"$method-name"(|c);
}
}
class Privateed does Privateer {
method !private() { return "⌣" }
}
my $obj = Privateed.new;
say $obj.test-private-method( "private" );
The key here is to call a method by name, which you can do with public and private methods, although for private methods you need to use their special syntax self!.

Catching up with 'require' for a metamodel class

I have defined own metamodel class to create a special kind of classes. Now, I would like these classes to automatically register themselves with a special kind of manager. Basically, this would like like this (would only compose be called each time when class' module is being loaded):
use MyManager;
class MyHOW is Metamodel::ClassHOW {
method compose ( Mu \type ) {
self.add_parent( type, MyParentClass );
callsame;
registerMyClass( type );
}
}
Then I have something like:
use v6;
use MyClass;
myclass Foo { ... }
in a module. Then there is a manager object which scans repositories/file system and requires modules with names matching to a certain pattern. Afterwards, it needs to know what myclasses are defined in each module. It could scan the symbol table of the loaded module. But this won't work if the loaded file contains multiple modules or no modules at all – like in the example above.
So far, it looks like the INIT phaser would provide the solution, but I'm struggling to find how to get the body block of a class from within the composer method.
When doing meta-programming, the meta-object's methods are invoked during compilation, as declarations are parsed. Therefore, the compose method is called immediately after the parsing of a myclass foo { } declaration. The result of the module's compilation is then saved, and nothing in the meta-object will be processed again when the module is loaded.
There's no supported way that I'm aware of to inject a load-time callback into the module where a type is being declared. However, it's possible to install the symbols into a separate package - used as a registry - and then find them there.
For example, given I have a lib/MyClass.pm6 that looks like this:
package MyRegistry { }
class MyParentClass { }
class MyHOW is Metamodel::ClassHOW {
method compose ( Mu \type ) {
MyRegistry::{self.name(type)} = type;
self.add_parent( type, MyParentClass );
callsame;
}
}
my package EXPORTHOW {
package DECLARE {
constant myclass = MyHOW;
}
}
And I write some files mods/A.pm6 and mods/B.pm6 like this:
use MyClass;
myclass A { }
And this:
use MyClass;
myclass B { }
Then when I require them in a script like this, and dump the keys in MyRegistry, they'll both be registered there:
use MyClass;
for dir('mods', test => /pm6$/) {
require $_;
}
dd MyRegistry.WHO.values;
Thus giving a predictable way to find them all.
Note that for a technique like this to work, you really need to have them stored into a Stash, since the loader knows how to symbol-merge those, whereas other types touched in different ways during the compilation of different modules will result in load-time conflicts.
You are left with the slight challenge of making sure to install everything under a sufficiently unique key; the type name as I used here is probably not unique enough in general. Probably I'd just generate something sufficiently random that the chance of a collision is hugely unlikely.

Using public and private methods inside their class in Perl 6

If I have a public method, I can call it inside its class using both $.name and self.name:
class TEST {
has Int $.a;
method b($x) {
return $!a * $x;
}
method c($y) {
return self.b($y) * 3; # or $.b($y)
}
}
my $m = TEST.new(a => 10);
say $m.c(2); # 60
But if I make b a private method, I only can call it with self!b, not $!b, otherwise I get the following error message:
Attribute $!b not declared in class TEST
What's behind this rule? What are the rules of calling a method inside its own class?
An attribute can always be referred to as $!foo in a class. If you do that, than the code will be generated to directly access the attribute itself, and any classes subclassing your class will not be able to change this behaviour.
If you use has $.foo in the declaration of a class, it means that a public accessor (and if you add is rw it can also function as a mutator).
When you use $.foo in your code otherwise, it is exactly the same as $( self.foo ). This means that it will call the method foo on self, and itemize the return value (make it a single "thing" if it wasn't yet). This will go wrong if you defined your attribute with $!foo and you did not supply a method foo yourself.
This goes even further: $.bar really means self.bar: you only need to have a method existing by the name bar, which may not be related to any attribute at all.
If you define a private method !baz, the ! just indicates the privacy of the method, which means you need to call it indeed as self!baz. There is no short syntax for it.
Personally I dislike the fact that you can say $.zippo even if zippo is not an attribute. But I'm afraid that ship has sailed. But this behaviour is now causing you confusion :-(
So what's behind the rule for not having a short syntax for calling a private method? Not sure, I guess really that $!foo was already taken to mean direct access to the attribute, and provide you with a compile time error if the attribute doesn't exist.
Hope this answers your question!

Why can't I call meta methods on Routine::WrapHandle?

This is a continuing question from my previous one Why is Perl 6's unwrap method a method of Routine?, but mostly unrelated.
The wrap method is documented to return "an instance of a private class called WrapHandle. Besides that being odd for leaking a class that's private, it's not actually the name of the thing that comes back. The class is actually Routine::WrapHandle:
$ perl6
> sub f() { say 'f was called' }
sub f () { #`(Sub|140397740886648) ... }
> my $wrap-handle = &f.wrap({ say 'before'; callsame; say 'after' });
Routine::WrapHandle.new
But here's the question. I wanted to call .^methods on Routine::WrapHandle. That doesn't work:
> Routine::WrapHandle.^methods
Could not find symbol '&WrapHandle'
in block <unit> at <unknown file> line 1
This is the same as trying it on an undefined class name:
> Foo::Baz.^methods
Could not find symbol '&Baz'
in block <unit> at <unknown file> line 1
I can call meta methods on the instance though:
> $wrap-handle.^methods
(restore)
> $wrap-handle.^name
Routine::WrapHandle
What's going on there?
The definition of Routine::WrapHandle looks something like this:
my class Routine {
method wrap(&wrapper) {
my class WrapHandle { ... }
...
}
}
We can ignore the surrounding method; the important bit is that we're dealing with a lexical inner class defined within an outer class. Simplifying some more, we arrive at the following pattern:
package Foo {
my class Bar {}
say Bar.^name; #=> Foo::Bar
}
say try Foo::Bar; #=> Nil
The fully qualified name of the inner class will include the name of the enclosing package, but due to the explicit my (instead of the implicit our), the class will not be installed as a package variable and the lookup at file scope fails.

Create Method via GDSL script that has a delegating closure parameter

Using the (scarcely documented) gdsl scripts of Intellij, one can add dynamic methods to a class:
contributor(context(ctype: "my.Type")) {
method name: "doIt", params: [body: {}], type: void
}
One can also configure the delegation of a closure:
contributor(context(scope: closureScope())) {
def call = enclosingCall("doIt")
if (call) {
def method = call.bind()
def clazz = method?.containingClass
if (clazz?.qualName == 'my.Type') {
delegatesTo(findClass('my.Inner'))
}
}
}
Which, when doIt is a method that is defined in the code (not dynamically added), also works as designed.
However, when using the closureScope with the previously created method, the containing class method is always null, meaning that I can not safely delegate inside the closure to the addressed my.Inner class.
What I want is adding a dynamic method equivalent to:
void doIt(#DelegatesTo(my.Inner) Closure)...
I.e. I want the method to be available in code completion (this works), and inside the so created closure, I want correct code completion when addressing methods of my.Inner.
So far, I tried various approaches:
include the #DelegatesTo annotation in the param definition
try more esoteric approaches in finding the owner of the closure, which fails because the GrMethodCall simply has no parent
unconditionally delegating all closures named doIt to my.Inner which works, but is no viable solution since I do have multiple doIt methods (on different classes) delegating to different targets.
So, how can I make IDEA behave as expected and delegate to the correct target?
Edit to make it clearer:
Given the following classes:
package my
class Type {
void doIt(Closure) {}
}
class Inner {
void inInner() {}
}
and the following gdsl:
contributor(context(scope: closureScope())) {
def call = enclosingCall("doIt")
if (call) {
def method = call.bind()
def clazz = method?.containingClass
println clazz?.qualName
if (clazz?.qualName == 'my.Type') {
delegatesTo(findClass('my.Inner'))
}
}
}
when I start typing in a new script:
new Type().doIt {
inInner()
}
When inside the closure, I get the following:
code completion for inInner
inInner is shown as valid
The console output when started with idea.bat from commandline shows the line my.Type (from the println)
Ctrl-B on inInner correctly links to source code.
(The same behaviour can be reached without the gdsl when annotation the Closure Parameter in the doIt method with #DelegatesTo(Inner))
However, I do not want to manually include the doIt method in the source of Type, it is generated by an AST Transformation, so my source file now looks like this:
package my
class Type {
}
class Inner {
void inInner() {}
}
I can tell IntelliJ about the new method using the following gdsl snippet
contributor(context(ctype: "my.Type")) {
method name: "doIt", params: [body: {}], type: void
}
Now the IDE correctly recognizes the doIt method with a closure parameter. However, inside the Closure, the following happens:
sometimes code completion shows inInner, sometimes after changing something, it does not (when using the original code to fix a type, it was shown, but later declared "unresolved", after going through the code changes of this edited example, it is not shown anymore...)
Even when shown, inInner is shown with "cannot resolve symbol" decoration
the console shows null as clazz, i.e. the method is found, but not linked to an owner ASTNode
Ctrl-B does not link to the corresponding method in Inner
So what I want is the same behaviour for an injected doIt method (via Gdsl) as with a method included in the source, i.e. I want the gdsl to inject a doIt method with a delegating closure (to Inner) into the type class.
This worked for me adding the ctype to scope insted of finding the class type from the method
contributor(context(scope: closureScope(), ctype: 'my.Type')) {
def call = enclosingCall("doIt")
if (call) {
delegatesTo(findClass('my.Inner'))
}
}