When I make a class method that starts with a ^, and I try to invoke it, it gives me an error.
class C {
method ^test () {
"Hi"
}
}
dd C.new.test;
Too many positionals passed; expected 1 argument but got 2
in method test at .code.tio line 1
in block <unit> at .code.tio line 1
If I make the same method without a leading ^, it works fine.
class C {
method test () {
"Hi"
}
}
dd C.new.test;
"Hi"
I've seen modules expose classes with methods that do start with a ^, which leads to my question. Why am I getting this error when I define a method name that starts with a ^?
TL;DR The method is getting invoked properly. The ^ in foo.^bar indicates a "metamethod". This is neither an instance method nor a class method. Metamethods are passed both an invocant, as is the case for all methods, and another "original invocant" object as the first argument.
Most users will never need to think about this stuff. But you've asked, so let's dig in...
Metamethods
Quoting the Meta-object protocol (MOP) Raku doc page:
Raku is built on a meta object layer.
This MOP layer defines various built in "metamethods" that you can use.
For example:
say .^attributes given class bar { has Int $!foo }
This displays (Int $!foo). The .^attributes method call is a metamethod. It's called on the (typically invisible) metaobject that determines how a Raku type works behind the scenes. In this case, it returns the attributes (has variables) of a class.
But there can also be user defined metamethods. One way to declare these is in an otherwise ordinary class:
class {
has Int $!foo;
method ^attributes ($arg) { self, $arg }
}
say baz.^attributes
The above baz class includes an ^attributes metamethod declaration that overrides the built in metamethod. Of special note, I've added an argument. ALL metamethods get at least one argument (in addition to a regular invocant).
With this declaration, instead of (Int $!foo) in response to an .^attributes call you instead get the list self, $arg from the .^attributes method in the baz class.
Note how self is neither a baz instance object nor a baz type object -- instead it's Perl6::Metamodel::ClassHOW+{<anon>}.new -- whereas $arg is a baz (type) object.
The rest of this answer explains what's going on in more detail.
A recap of an ordinary method call
First, let's recap a typical method call.
The syntax foo.bar results in a "bar" method (message) being dispatched to foo.
If foo is an instance of a class, "bar" is dispatched to that instance. Such a method call is sometimes referred to as an "instance method".
If foo is a type object corresponding to a class, "bar" is dispatched to that type object. Such a method call is sometimes referred to as a "class method".
In both cases, "bar" is dispatched to foo.
#foo.^bar
The syntax foo.^bar is different.
Read the ^ as pointing up to another object that hovers invisibly above foo, or indeed anything related to the kind of type foo is.
Such objects are HOW objects that determine How Objects Work. These HOW objects typically stay invisible, making things work nicely, with users blissfully unaware of their existence and the work they're doing.1
A method call of the form foo.^bar ordinarily results in Raku dispatching a metamethod call to foo's HOW object.
These metamethods require two invocant-like arguments. There's the HOW object. This is passed as the regular invocant. Then there's the foo object. This is passed as a first ordinary argument to the metamethod.
So that's what ordinarily happens when you call foo.^bar -- a metamethod call is dispatched to foo's HOW object and foo is passed as an ordinary argument storing what could be said to be "the original invocant".
#foo.^bar when there's no built in .^bar metamethod
If you call foo.^bar when there's no such method you'll get an error:
42.^bar
yields:
No such method 'bar' for invocant of type 'Perl6::Metamodel::ClassHOW'
Note how the invocant type is a metamodel class, not 42 or Int.
If a user defined class declares a ^.bar, then Raku calls it, passing the instance/class's HOW object as the invocant and the "original invocant" (foo) as the first ordinary argument:
class foo {
method ^bar ($arg) { self, $arg }
}
say foo.^bar; # (Perl6::Metamodel::ClassHOW+{<anon>}.new (foo))
Footnotes
1 Calling .HOW on an object returns its HOW:
say .HOW given class {} # Perl6::Metamodel::ClassHOW
HOW objects are part of the MOP, a layer deep down inside Raku.
Most devs will never need to explicitly dig down to this level.
If you dig even deeper you're leaving specified Raku. In Rakudo the .HOW of a HOW object is typically an NQP object:
say ((.HOW.new given class {}).HOW).^name; # NQPClassHOW
Related
I am using ByteBuddy to generate a class.
Prior to working with DynamicType.Builder, I was going to store a MethodCall as an instance variable:
private final MethodCall frobCall =
MethodCall.invoke(ElementMatchers.named("frob")); // here I invoke a method I'm going to define as part of the instrumented type
Then later in my generation logic for the instrumented type I define the frob method to do something:
.defineMethod("frob")
.intercept(...etc....) // here I define frob to do something
…and I define the (let's say) baz method to invoke frob:
.defineMethod("baz")
.withParameter(...) // etc.
.intercept(frobCall); // invokes "frob", which I've just defined above
(I am trying to keep this simple and may have mistyped something but I hope you can see the gist of what I'm trying to do.)
When I make() my DynamicType, I receive an error that indicates that the dynamic type does not define frob. This is mystifying to me, because of course I have defined it, as you can see above.
Is there some restriction I am unaware of that prohibits ElementMatchers from identifying instrumented type methods that are defined later? Do I really have to use MethodDescription.Latent here?
It should match all methods of the instrumented type. If this is not happening as expected, please set a breakpoint in MethodCall.MethodLocator.ForElementMatcher to see why the method is not showing up. I assume it is filtered by your method matcher.
I noticed however that it did not include private methods which is now fixed and will be released within Byte Buddy 1.10.18.
For CALL METHOD - Static Method Call (Obsolete), the ABAP keyword documentation says: "If CALL METHOD is used for the standalone method call, no chained method calls are possible ..."
Nevertheless, the following happily executes on an 7.40 system. Isn't that an example of a standalone method call? Or else, what am I getting wrong?
REPORT ZUTEST3.
CLASS class_parent Definition.
PUBLIC Section.
METHODS m1 returning value(r) type ref to class_parent.
ENDCLASS.
CLASS class_parent Implementation.
Method m1.
create object r.
write / 'm1'.
EndMethod.
ENDCLASS.
start-of-selection.
data cl type ref to class_parent.
CREATE OBJECT cl.
CALL METHOD cl->m1( )->m1( ).
Edit: Disclaimer
We are writing a tool in Java that parses and transforms ABAP code. In particular, we have no intention to write new ABAP code. But instead, our tool has to handle all of ABAP, even obsolete statements and obscure syntax variants. Furthermore, I'd like to mention that I'm not an ABAP expert.
Addendum February 23rd, the right answer is given by Florian in the comments : "I reported the bug to the docu team and they answered that it has already been reported and they corrected it in the latest version. The new statement is: "With the second variant without round brackets, chained method calls are not possible and the operators NEW and CAST cannot be used.""
I let my original answer below (by the way, I think now that in CALL METHOD static_meth..., the term "standalone method call" refers to the part "static_meth", so it refers to the two groups of constructs, hence my answer is inexact and the one by SAP is 100% correct)
As I can see, the documentation says that the term "standalone method call" refers to these constructs (observe the use of parentheses), which are declared obsolete :
CALL METHOD method( ).
CALL METHOD method( 25 ).
CALL METHOD method( a = 1 ).
CALL METHOD method( EXPORTING a = 1 ).
CALL METHOD instance->method( ).
CALL METHOD class=>method( ).
etc.
The term ""standalone method call" doesn't refer to these constructs :
CALL METHOD method.
CALL METHOD method EXPORTING a = 1.
CALL METHOD instance->method.
CALL METHOD class=>method.
etc.
I guess that CALL METHOD cl->m1( ) belongs to the first group of constructs so there's an error in the documentation.
Probably a not is missing because it should apply to the second group of constructs (for instance, CALL METHOD method->method( ) is invalid).
Conclusion by me: you should read "If CALL METHOD is not used for the standalone method call, no chained method calls are possible ..."
Conclusion by Florian & SAP: In the comments below, Florian has asked the SAP support and indicates which exact sentence SAP should use in the next official release of the documentation
ADDENDUM (read it if you think wrongly that the documentation page is about "static methods", I hope I will make it clear that it's not).
The answers in this question prove that the documentation "CALL METHOD - Static Method Call (Obsolete)" is quite confusing.
The documentation title: here "static method call" means "static call of methods", not "call of static methods" (while at other places it could possibly have this meaning). If we could add parentheses in the written language, that would give these two possibilities, respectively :
static (method call) : static call of a method (whatever this method is of type "static" or "instance"; we could have a static call of an instance method)
(static method) call : call of a static method
Definitions :
static call : the class, interface or method names are "hardcoded" as symbols in the source code, not text literals, so that they are known by the compiler (for instance, CALL METHOD class=>method.). The opposite, a dynamic call, means that the names are passed through variables, which are only known at runtime (for instance, DATA classvar TYPE seoclsname VALUE 'CL_ABAP_TYPEDESCR'. CALL METHOD (classvar)=>(methodvar).) This other documentation page shows well that "static method call" is opposed to "dynamic method call", it never talks about "static and instance methods", only about "static method call" and "dynamic method call".
static method : a method being declared with CLASS-METHODS. For instance, a static call could be cl_ixml=>create( ), and a dynamic call could be DATA classvar TYPE seoclsname VALUE 'CL_IXML'. CALL METHOD (classvar)=>create.
Something in the documentation which confused me too, is the use of the term "static method" and examples based on static method only, because in fact the documentation page is about "static call", not static methods (instance methods could have been used) :
Syntax : CALL METHOD { static_meth( ) | static_meth( a ) | ... : what does mean "static_meth" here? In fact "static_meth" doesn't mean that it's a static method but it's any method in the context of a static method call. If you look at the documentation pages about "static calls" and "dynamic calls", you will see that the syntax is respectively static_meth( ) ... and CALL METHOD dynamic_meth ...
Example : a static method is again used in the three calls, all three of them having the same exact meaning but written with different syntaxes, to demonstrate that the first two calls are obsolete, and only the third one is recommended. In fact all three examples should have better used an instance method to avoid the confusion!
First of all, the method m1 is not static in your example and the quotation from the documentation has it that it is about a static method (CLASS-METHOD).
The only possible thing might be like in this example.
REPORT zutest3.
CLASS class_parent DEFINITION.
PUBLIC SECTION.
METHODS m1 RETURNING VALUE(r) TYPE REF TO class_parent.
CLASS-METHODS m1_static RETURNING VALUE(r) TYPE REF TO class_parent.
ENDCLASS.
CLASS class_parent IMPLEMENTATION.
METHOD m1.
CREATE OBJECT r.
WRITE / 'm1'.
ENDMETHOD.
METHOD m1_static.
CREATE OBJECT r.
WRITE / 'm2'.
ENDMETHOD.
ENDCLASS.
START-OF-SELECTION.
* this seems to be possible but no one sane calls a static method on an object reference
CALL METHOD class_parent=>m1_static( )->m1_static( ).
* the following two are not possible and will not compile either
* CALL METHOD class_parent=>m1_static( )=>m1_static( ).
* class_parent=>m1_static( )=>m1_static( ).
Second of all the CALL METHOD statement in this case is just a redundancy and its role is only informative.
Those two are equivalents
CALL METHOD cl->m1( ).
cl->m1( ).
analogically to for example this
DATA i TYPE i.
COMPUTE i = i + 1.
i = i + 1.
A bug in the docu. I reported it to the docu team and they answered that it has already been reported and they corrected it in the latest version.
The new statement is:
With the second variant without round brackets, chained method calls
are not possible and the operators NEW and CAST cannot be used.
When I running my (proprietary) code, I get this:
info object isa class DlgClass is:1
DlgClass does not refer to an object
while executing
"::oo::Obj6::my Set DlgClass"
("uplevel" body line 1)
invoked from within
"uplevel 1 [list [namespace which my] Set $args]"
(class "::oo::Slot" method "-set" line 2)
invoked from within
"::oo::Obj6::my --default-operation DlgClass"
("uplevel" body line 1)
invoked from within
"uplevel 1 [list [namespace which my] $def {*}$args]"
(class "::oo::Slot" method "unknown" line 6)
invoked from within
"superclass DlgClass"
(in definition script
The two lines of code ( 1 which generate the message, and the one before it ) are:
puts "info object isa class DlgClass is:[info object isa class DlgClass]"
superclass DlgClass
I don't understand. Doesn't the first line output (the info object isa class DlgClass is:1one) indicate that the superclass is indeed defined?
TclOO definitions care very strongly what the current stack frame is because the oo::define command uses a private variable in the internal call frame structure to store the identity of the class being defined. For some reason (that I find a bit surprising) the source command is interfering with this.
The simplest workaround that might possibly work is to write your own. Fortunately, you can just put it in the right namespace and have it work in the place where you really need it:
# Very simple version of the standard [source] command
proc ::oo::define::source {filename} {
set f [open $filename]
set script [read $f]
close $f
tailcall eval $script
}
On the other hand, your script works for me when I try it even without my special version of source. Something else is going on.
Apparently it happens when I do lazy evaluation of the superclass' definition from within the class block.
i.e.
rather than writing:
oo::class create foo {
source "DlgClass.tcl" ;# if needed
superclass DlgClass
}
One needs to do:
source "DlgClass.tcl" ;# if needed
oo::class create foo {
superclass DlgClass
}
And then it works. I think that it may be a bug in oo::class/tcl engine, but, as it is so easy to workaround, not a major one.
instance creation method, like
ClassName new
To aid with some details,
we could write a = arithmetic method in abstract class,
then doubledispatch them in subclasses.
could we use that in instance creation?
I have tried new but it fail. Leads to some predefined basic new method.
Double Dispatch doesn't really make sense in the new case. The idea behind double dispatch, is that you can't determine the correct behavior by dispatching only to the receiver. The type of the (single) argument has an equal effect on which behavior is chosen (dispatched). In other words, double dispatch only makes sense if you have arguments to your methods, new being unary, it does not.
That said, you can certainly implement your own new method that overrides the stock default inherited one. And you can make it do all kinds of interesting things. It's common to do some sort of environment check to determine what subclass is appropriate.
AbstractClass>>>new
^self platform = #unix
ifTrue: [SubclassThatLeveragesUnix basicNew]
ifFalse: [CrappyPCSubclass basicNew]
Note that we use basicNew here, rather than new. If you used new you would need to either implement distinct overrides in those subclasses, otherwise it would just inherit and resend the AbstractClass>>>new message again.
... or you could do something like:
AbstractClass class>>#new
^ (self platform concreteClassFor: self) basicNew initialize.
which is basically same idea, but without ifs :)
The key point of double dispatch is that by swapping the receiver and the argument of the primary message, you call a second time a virtual call and that you get then the effect of selecting a method based on the message receiver and its argument. Therefore you need to have message with argument.
Here is a typical example of double dispatch: addition integer and floats and performing adequate conversion.
Integer>>+ arg
^ arg sumFromInteger: self
Float>>+ arg
^ arg sumFromFloat: self
Integer>>sumFromInteger: anInt
<primitive adding to ints>
Integer>>sumFromFloat: aFloat
^ self asFloat + aFloat
Float>>sumFromFloat: aFloat
<primitive adding two floats>
Float>>sumFromInteger: anInt
^ self + anInt asFloat
Now 1 + 1.0 will hit first + on Integer then sumFromInt: then + then sumFromFloat. Note that we have enough information so we could shortcut the second + invocation,
What the example shows is that during the first call, the dynamic message resolution finds on method (so it defining like a dynamic case) and then by swapping the argument and the receiver, the dynamic message resolution performs another case based on the arg. So at the end you get a method selected using the two objects of the original call.
Now about your question: In Pharo class messages are dynamically looked up so you could implement instance creation methods using double dispatch without problem but the goal is unclear.
MyClass class>>newWith: arg
arg newFromMyClass: aClass
In a MATLAB class I am writing, if the constructor is given 0 arguments, the user is asked to provide a file using uigetfile. If the user cancels the prompt, uigetfile returns 0. In that case it makes no sense to make the object. Is there some way of cancelling the object construction without throwing an exception? If I do an early return I get a malformed object that cannot be used. Here is what the code looks like:
classdef MyClass
methods
function self = MyClass(filename)
if nargin == 0
filename = uigetfile;
if filename == 0
%cancel construction here
return; %I still get a MyClass object with self.filename == []
end
end
self.filename = filename;
end
end
properties
filename;
end
end
However I am unsure if using uigetfile in the constructor is the right thing to do. Maybe it should be the resposibility of another part of my code.
In modern Matlab objects, I don't think it's possible to get out of the constructor without either returning a constructed object or throwing an error. (In the old style classes, the constructor was actually allowed to return whatever it wanted, including objects or primitives of other types, and oh man could that turn in to a mess.) When a constructor is called, the output argument is already intialized with an object with the default property values, so when you call return there, it just skips the rest of initialization and returns the object. If you try to replace out with something besides a MyClass object, that's an error.
Just reorganize the control flow to pull the GUI code out of the constructor, like you're speculating on at the end. Mixing it in to the constructor, especially conditionally, can cause problems. In particular, Matlab expects the zero-arg constructor to always return a scalar object with some sort of default values, because the zero-arg gets called implicitly when filling in elements during array expansion and so on. It's basically used as a prototype.