Uninstantiable; Callable when using Callable variables from a trait declaration - traits

With this code, I'm trying to add a "logging" trait to a subroutine:
my &loggr = -> $event {
state %store;
%store{ DateTime.new( now ) } = $event;
}
multi sub trait_mod:<is>(Sub $s, :$logger){
loggr( $s.name );
}
multi sub add( Int $a, Int $b) is logger {
$a + $b;
}
say add(1,2);
However, I get the error:
===SORRY!=== Error while compiling /home/jmerelo/Code/perl6/my-perl6-examples/is-logger-fail.p6
Cannot invoke this object (REPR: Uninstantiable; Callable)
at /home/jmerelo/Code/perl6/my-perl6-examples/is-logger-fail.p6:14
(line 14 would be the line where add is declared). Declaring loggr directly as a sub yields no error. Why am I getting this Uninstantiable error here?

Why am I getting this Uninstantiable error here?
When used after a my declaration, = (or :=) invokes assignment (or binding) at run-time. A trait applied to a compile-time sub declaration runs at compile-time, which comes first. So your trait calls loggr before it's initialized.
To fix that, you need to shift the variable initialization to compile-time, eg:
BEGIN &loggr = ...
or
constant &loggr = ...
While the error message reads like a low-level error, and it would be nice if it specifically mentioned loggr (but perhaps doesn't because it's low-level), it will hopefully make more sense now:
===SORRY!=== Error while compiling ...
Cannot invoke this object (REPR: Uninstantiable; Callable)
Your code has asked to call loggr at compile-time. But while it does have the appropriate Callable type, it hasn't yet been initialized, so is uninstantiable at the time it is being asked to invoke it.

Related

Dynamically created class with invocant constraint

Official docs says that class can be built dynamically like so:
constant A := Metamodel::ClassHOW.new_type( name => 'A' );
A.^add_method('x', my method x(A:D:) { say 42 });
A.^compose;
A.new.x(); # x will be only called on instances
But what if I am building a class and don't assign it to a constant but rather store it in a var (for instance when I need to create a bunch of classes in loop) like so:
my $x = Metamodel::ClassHOW.new_type( name => 'some custom string' );
$x.^add_method('x', my method ($y:) { say $y });
$x.^compose;
But in this case I can call method x both on class ($x.x) and on instance ($x.new.x) though I want it to only be called on instances.
I tried to define method like so:
$x.^add_method('x', my method ($y:D:) { say $y });
but that produces an error:
Invalid typename 'D' in parameter declaration.
Of course I can check defindness of the value inside the method but I want some compile-time guarantees (I want to believe that type checking is done in compile time).
I tried to play with signatures and parameters but couldn't find a way to create an invocant parameter but what is more important I am not sure how to assign signature which I have in a variable to some method.
Change:
my $x = ...
to:
my constant x = my $ = ...
In full:
my constant x = my $ = Metamodel::ClassHOW.new_type( name => 'some custom string' );
x.^add_method('x', my method (x:D $y:) { say $y });
x.^compose;
x = Metamodel::ClassHOW.new_type( name => 'another custom string' );
...
I want some compile-time guarantees (I want to believe that type checking is done in compile time).
By making the constant's RHS be a variable declaration, you blend static compile-time aspects with dynamic run-time ones.
(BTW, the my before constant is just me being pedantic. A plain constant like you've used is equivalent to our constant which is less strict than my constant.)
I note the error message for a non-instance is different:
Type check failed in binding to parameter '$y';
expected type some custom string cannot be itself
At a guess that's because the usual message comes from Mu or Any and your class isn't inheriting from either of them.
I am not sure how to assign signature which I have in a variable to some method.
I'll leave that part unanswered.
The best way I can think of to produce a method with types substituted into a signature with Raku today is to use a parametric role to help out, like this:
my role Helper[::T] {
method foo(T $inv:) {}
}
my &meth = Helper.^parameterize(Int).^pun.^lookup("foo");
say &meth.signature;
Which outputs (Int $inv: *%_). Substitute Int with the type you are building.

How to use spyk

I need to verify that a method has been called on an object. So I make a spy of this object:
obj = spyk(obj)
And then I verify that a method has been called:
verify(exactly = 1) { obj.f(3) }
The test fails with the following error message:
java.lang.AssertionError: Verification failed: call 1 of 1: obj(#2).f(eq(3))) was not called.
However I can clearly see the method f being called:
I can break in that method in debug mode
I print out hello world from f() in that method and see it being printed.
How do I use spies correctly in mockk?
P.S.
I tried doing val obj = spyk(obj()) but I get lateinit has not been initialized error because I need to set a parameter in obj as so:
obj.setDependency(friend)
But in the case where I first do val obj = spyk(obj()) and then call obj.setDependency(friend) like I explained above I end up with a lateinit has not been initialized error
Can someone please help me resolve this issue?
In your case, I don't understand this part obj = spyk(obj). What are you doing here? I think this part don't even compile.
You receive lateinit has not been initialized error because spyk(obj()) calls real constructor. If your object has dependencies, you have to create them too or pass mockk instead of them.
According to the documentation:
Note: the spy object is a copy of a passed object.
You have to create this object like a normal object, so all dependencies have to be filled.
I am using spyk in this way, let me show you a quick example.
fun `should call method testMethod`() {
val spy = spyk<TestClass>()
spy.testMethod(1)
verify (exactly = 1) { spy.testMethod(1) }
}

Why the variable can't be initialized correctly in inline function as in java?

We know the lambda body is lazily well, because if we don't call the lambda the code in the lambda body is never be called.
We also know in any function language that a variable can be used in a function/lambda even if it is not initialized, such as javascript, ruby, groovy and .etc, for example, the groovy code below can works fine:
def foo
def lambda = { foo }
foo = "bar"
println(lambda())
// ^--- return "bar"
We also know we can access an uninitialized variable if the catch-block has initialized the variable when an Exception is raised in try-block in Java, for example:
// v--- m is not initialized yet
int m;
try{ throw new RuntimeException(); } catch(Exception ex){ m = 2;}
System.out.println(m);// println 2
If the lambda is lazily, why does Kotlin can't use an uninitialized variable in lambda? I know Kotlin is a null-safety language, so the compiler will analyzing the code from top to bottom include the lambda body to make sure the variable is initialized. so the lambda body is not "lazily" at compile-time. for example:
var a:Int
val lambda = { a }// lambda is never be invoked
// ^--- a compile error thrown: variable is not initialized yet
a = 2
Q: But why the code below also can't be working? I don't understand it, since the variable is effectively-final in Java, if you want to change the variable value you must using an ObjectRef instead, and this test contradicts my previous conclusions:"lambda body is not lazily at compile-time" .for example:
var a:Int
run{ a = 2 }// a is initialized & inlined to callsite function
// v--- a compile error thrown: variable is not initialized yet
println(a)
So I only can think is that the compiler can't sure the element field in ObjectRef is whether initialized or not, but #hotkey has denied my thoughts. Why?
Q: why does Kotlin inline functions can't works fine even if I initializing the variable in catch-block like as in java? for example:
var a: Int
try {
run { a = 2 }
} catch(ex: Throwable) {
a = 3
}
// v--- Error: `a` is not initialized
println(a)
But, #hotkey has already mentioned that you should using try-catch expression in Kotlin to initializing a variable in his answer, for example:
var a: Int = try {
run { 2 }
} catch(ex: Throwable) {
3
}
// v--- println 2
println(a);
Q: If the actual thing is that, why I don't call the run directly? for example:
val a = run{2};
println(a);//println 2
However the code above can works fine in java, for example:
int a;
try {
a = 2;
} catch (Throwable ex) {
a = 3;
}
System.out.println(a); // println 2
Q: But why the code below also can't be working?
Because code can change. At the point where the lambda is defined the variable is not initialized so if the code is changed and the lambda is invoked directly afterwards it would be invalid. The kotlin compiler wants to make sure there is absolutely no way the uninitialized variable can be accessed before it is initialized, even by proxy.
Q: why does Kotlin inline functions can't works fine even if I initializing the variable in catch-block like as in java?
Because run is not special and the compiler can't know when the body is executed. If you consider the possibility of run not being executed then the compiler cannot guarentee that the variable will be initialized.
In the changed example it uses the try-catch expression to essentially execute a = run { 2 }, which is different from run { a = 2 } because a result is guaranteed by the return type.
Q: If the actual thing is that, why I doesn't call the run directly?
That is essentially what happens. Regarding the final Java code the fact is that Java does not follow the exact same rules of Kotlin and the same happens in reverse. Just because something is possible in Java does not mean it will be valid Kotlin.
You could make the variable lazy with the following...
val a: Int by lazy { 3 }
Obviously, you could use a function in place of the 3. But this allows the compiler to continue and guarantees that a is initialized before use.
Edit
Though the question seems to be "why can't it be done". I am in the same mind frame, that I don't see why not (within reason). I think the compiler has enough information to figure out that a lambda declaration is not a reference to any of the closure variables. So, I think it could show a different error when the lambda is used and the variables it references have not been initialized.
That said, here is what I would do if the compiler writers were to disagree with my assessment (or take too long to get around to the feature).
The following example shows a way to do a lazy local variable initialization (for version 1.1 and later)
import kotlin.reflect.*
//...
var a:Int by object {
private var backing : Int? = null
operator fun getValue(thisRef: Any?, property: KProperty<*>): Int =
backing ?: throw Exception("variable has not been initialized")
operator fun setValue(thisRef: Any?, property: KProperty<*>, value: Int) {
backing = value
}
}
var lambda = { a }
// ...
a = 3
println("a = ${lambda()}")
I used an anonymous object to show the guts of what's going on (and because lazy caused a compiler error). The object could be turned into function like lazy.
Now we are potentially back to a runtime exception if the programmer forgets to initialize the variable before it is referenced. But Kotlin did try at least to help us avoid that.

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.

A constructor with only 1 argument in Perl 6

I want to override new so that my class can be created only by passing one argument to the constructor, no more and no fewer.
class MyClass {
has $.var1;
method new($var1) {
return MyClass.new(var1 => $var1);
}
}
my $my_class1 = MyClass.new(33);
say $my_class1.var1;
The error is:
Too few positionals passed; expected 2 arguments but got 1
in method new at test1.pl6:28
in method new at test1.pl6:28
in block <unit> at test1.pl6:33
What's up with it?
Custom constructors need to call bless, ie
class MyClass {
has $.var1;
method new($var1) {
return self.bless(var1 => $var1);
}
}
There are a few things that can be improved, eg
one could add an explicit invocant parameter and use :U to make .new() fail when called on instance objects
the explicit return is superfluous - the last expression within the method will be returned anyway, and currently, it actually hurts performance
there's syntactic sugar for passing a named argument held in a variable of the same name
Putting it all together, we end up with
class MyClass {
has $.var1;
method new(MyClass:U: $var1) {
self.bless(:$var1);
}
}
As to where your error comes from:
Your method new is declared to take a positional argument (giving a total count of 2 expected arguments due to the implicit invocant), but the call MyClass.new(var1 => $var1) only passed a named one. Note that said method is the only .new() present in your class, so if the call had actually worked, you would have ended up with infinite recursion!