Lets presume we have two classes as follows:
oo::class create InsideThing {
constructor {} {
puts "Made a [self] that is an InsideThing"
}
destructor {
puts "Deleted a [self] that is an InsideThing"
}
method echo {text} {
puts "[self]: $text"
}
}
oo::class create Container {
constructor {} {
puts "Created a [self] that is a Container"
InsideThing create inner1
InsideThing create inner2
}
destructor {
puts "Deleted a [self] that is a Container"
}
method echo1 {text} {
# how to do something like this:
$inner1 echo $text
}
}
How would I go about accessing those inner objects? I want to do something like the following:
set c [Container new]
# (1) accessing inner1 indirectly
$c echo1 "Hallo World"
# (2) accessing inner1 dirctly
$c inner1 echo "Hallo World"
Is there a way to do that? Does this approache even make sense?
What I want to achieve is a nested object structure (essentially tree-like). I'd like to be able to navigate this structure by calling methods (e.g. parent, child) on the nodes. Also destroying the root should destroy all children (thats why I used create to create the nested objects inside the parent namespace)
To just plain use the contained object, just use its local name, which you should know because you created it in the constructor. It doesn't need to be held in a variable; it's entirely trivial to ensure it is a unique name because it will be in a unique namespace (the instance namespace; every TclOO object has its own private namespace for this sort of thing) and you control it completely.
Exposing the inner object is most easily done using forwarding methods.
TclOO comes with two main sorts of user-configurable methods: “normal” proc-like methods that you declare with method, and forwarding methods (a bit like an interp alias) that you declare with forward. The thing that you forward to is resolved with respect to the instance namespace, which is ultra-useful!
In particular, you can make a forward method on the container class that forwards to the relevant inner object. Like this (InsideThing is unchanged):
oo::class create Container {
constructor {} {
puts "Created a [self] that is a Container"
InsideThing create innerABC
InsideThing create innerDEF
}
destructor {
puts "Deleted a [self] that is a Container"
}
method echo1 {text} {
# Just use the name. That's all.
innerABC echo $text
}
forward inner1 innerABC
forward inner2 innerDEF
}
Then you can call into the inner object like:
$c inner1 echo "Yo!"
Or you can use the intermediating normal method like this:
$c echo1 "Hiya!"
It's up to you. (The forwarded version is around 20% faster in my informal testing with a modified version of your code with trivial do-nothing echo method implementation. However, real code will notice the difference significantly less; microbenchmarking is rarely all that useful in reality.)
Related
class A { has $.name; };
class B is A { submethod BUILD { $!name = 'foo' } };
This code looks natural but throws error.
Attribute $!name not declared in class B
Yes, it is not declared in class B, but we are in the partially constructed object during B::BUILD and documentation says that bless creates the new object, and then walks all subclasses in reverse method resolution order. So $!name attribute should be known for class B in this phase, right?
Is there any way to set parent class attributes during object construction without using new method? I know that new will do the trick here, but BUILD has a lot of syntactic sugar and BUILD / TWEAK feel more DWIMy and straightforward than resolving to low-level blessing in new.
Private attribute syntax ($!foo) is only available for attributes that are lexically visible. That's why they're private :-)
If class A would want other classes be able to change, it would need to provide a mutator method explicitely or implicitely (with is rw).
Or you could let class A trust class B as described at https://docs.raku.org/routine/trusts#(Type_system)_trait_trusts .
Still it feels you would do better using roles:
role A {
has $.name is rw;
}
class B does A {
submethod BUILD { $!name = 'foo' }
}
The other option is to use the is built trait on attributes that you would like the default constructor to initialize.
Consider the following:
class A {
has $.name is built
}
class B is A { }
B.new(name => "Foo").gist.say; # B.new(name => "Foo")
This allows descendend classes to use the named parameter matching the attribute in .new to initialize the value at object creation time. Please note that this will work whether the attribute is public "$." or private "$!".
Hope that helps!
TL;DR All attributes are technically private. This design is a good one. You could just call a method in A from B. There are, of course, other options too.
Why doesn't BUILD see parent class attributes?
Quoting Wikipedia Fragile base class page problem:
One possible solution is to make instance variables private to their defining class and force subclasses to use accessors to modify superclass states.¹
Hence, per Raku Attributes doc:
In Raku, all attributes are private, which means they can be accessed directly only by the class instance itself.
B can call a method in A
This code looks natural:
class A { has $.name }
class B is A { submethod BUILD { $!name = 'foo' } }
Quoting again from Raku doc section linked above:
While there is no such thing as a public (or even protected) attribute, there is a way to have accessor methods generated automatically: replace the ! twigil with the . twigil (the . should remind you of a method call).
Your code generates a $!name attribute (private to A) plus a public .name method. Any code that uses the A class can call its public methods.
Your code hasn't used the autogenerated accessor method. But it could have done so with a couple small changes:
class A { has $.name is rw } # Add `is rw`
class B is A { submethod BUILD { self.name = 'foo' } } # s/$!name/self.name/²
say B.new # B.new(name => "foo")
is rw makes the public .name accessor method a read/write one instead of the default read only one.
Not using is rw
As I now understand from your first comment below, an is rw accessor is disallowed given your requirements. You can achieve any effect that a class supports via its public interface.
Let's first consider a silly example so it's clear you can do anything that any methods can do. Using, say, self.name, in A or B, might actually run one or more methods in A that make a cup of tea and return 'oolong' rather than doing anything with A's $!name:
class A {
has $.name = 'fred'; # Autogenerates a `method name` unless it's defined.
method name { 'oolong' } # Defines a `method name` (so it isn't generated).
}
my \a = A.new;
say a; # A.new(name => "fred")
say a.name; # oolong
Conversely, if an A object changes its $!name, doing so might have no effect whatsoever on the name of the next cup of tea:
class A {
has $.name = 'fred';
method name { 'rooibos' } # ignores `$!name`
method rename { $!name = 'jane' }
}
my \a = A.new;
say a; # A.new(name => "fred")
a.rename;
say a.name; # rooibos
To recap, you can (albeit indirectly) do anything with private state of a class that that class allows via its public API.
For your scenario, perhaps the following would work?:
class A {
has $.name;
multi method name { $!name }
multi method name (\val) { once $!name = val }
}
class B is A {
submethod BUILD { self.name: 42 }
}
my \a = B.new;
say a; # B.new(name => 42)
say a.name; # 42
a.name: 99; # Does nothing
say a.name; # 42
Footnotes
¹ Continuing to quote solutions listed by Wikipedia:
A language could also make it so that subclasses can control which inherited methods are exposed publicly.
Raku allows this.
Another alternative solution could be to have an interface instead of superclass.
Raku also supports this (via roles).
² self.name works where $!name does not. $.name throws a different compiler error with an LTA error message. See Using %.foo in places throws, but changing it to self.foo works.
Sorry that my answer is late in the day, but I feel that your original question is very well pitched and would like to add my variation.
class A {
has $!name;
submethod BUILD( :$!name ) {}
multi method name { $!name }
multi method name(\v) { $!name := v }
method gist(::T:) { "{::T.^name}.new( name => $!name )" }
}
class B is A {
submethod BUILD( :$name ) { self.name: $name // 'foo' }
}
say B.new; #B.new( name => foo )
say A.new(name => 'bar'); #A.new( name => bar )
say B.new(name => 'baz'); #B.new( name => baz )
Raku OO tries to do two mutually incompatible things:
provide a deep OO (similar to C++ / Java)
provide a lightweight OO (similar to Python / Ruby)
This is done by having a core that does #1 and then adding some sugar to it to do #2. The core gives you stuff like encapsulation, multiple inheritance, delegation, trust relationships, role based composition, delegation, MOP, etc. The sugar is all the boilerplate that Raku gives you when you write $. instead of $! so that you can just throw together classes to be lightweight datatypes for loosely structured data.
Many of the answers here bring suggestions from mode #2, but I think that your needs are slightly too specific for that and so my answer tilts towards mode #1.
Some notes to elaborate why I think this is a good solution:
you state that you cannot use is rw - this avoids traits
with proper method accessors, you have control over initialization
BUILD() is not constrained by the public accessor phasing
no need to go to roles here (that's orthogonal)
And some drawbacks:
you have to write your own accessors
you have to write your own .gist method [used by say()]
It is attributed to Larry that "everyone wants the colon(:)". Well, he had the last say, and that the Raku method call syntax self.name: 'foo' echos assignment self.name= 'foo' is, in my view, no accident and meant to ease the mental switch from mode #2 to #1. ;-)
Does Raku succeed to reconcile the irreconcilable? - I think so ... but it does still leave an awkward gear shift.
EDITED to add submethod BUILD to class A
Thanks everyone for great discussion and solution suggestions. Unfortunately there is no simple solution and it became obvious once I understood how Raku constructs object instances.
class A {
has $.name is rw;
};
class B is A {
submethod BUILD {
self.A::name = 123; # accessor method is already here
}
};
B.new.name.say; # will print 123
So if inheritance is used Raku works from parent class to child class fully constructing each class along the way. A is constructed first, $.name param is initialized, public attribute accessor methods are installed. This A instance become available for B construction, but we are not in A build phase anymore. That initialization is finished. My code example shows what is happening with syntactic sugar removed.
The fact that
submethod BUILD {
self.name = 123;
}
is available in class B during BUILD phase does not mean that we (as class B) have this attribute still available for construction. We are only calling write method on already constructed class A. So self.name = 123 really means self.A::name = 123.
TL;DR: Attributes are not collected from parent classes and presented to BUILD in child class to be set at the same time. Parent classes are constructed sequentially and only their method interfaces are available in child BUILD submethod.
Therefore
class A {
has $.name; # no rw
};
class B is A {
submethod BUILD {
$!name = 123;
}
};
will not work because once we reach submethod BUILD in B class attribute $.name is already constructed and it is read only.
Solution for shallow inheritance:
Roles are the way to go.
role A {
has $.name;
};
class B does A {
submethod BUILD {
$!name = 123;
}
};
Roles are copied to class composing them, so class B sees this $.name param as their own and can initialize it. At the same time roles autopun to classes in Raku and standalone my $a = A.new( name => 123 ) can be used as a class.
However roles overdose can lead to orthogonal pattern issues.
Solution for deep inheritance:
There is none. You cannot have secure parent classes with read-only attribute behavior and initialize this attribute in child class builder, because at this moment parent class portion of self will be already constructed and attribute will be already read-only. Best you can do is to wrap attribute of parent class in private method (may be Proxy) and make it write-once this way.
Sad conclusion:
Raku needs improvement in this area. It is not convenient to use it for deep inheritance projects. Maybe new phaser is needed that will mash every attribute from parent classes in role-style and present them to BUILD at the same time. Or some auto-trust mechanism during BUILD. Or anything that will save user from introducing role inheritance and orthogonal role layout (this is doing stuff like class Cro::CompositeConnector does Cro::Connector when class Cro::Connector::Composite is Cro::Connector is really needed) to deep OO code because roles are not golden hammer that is suitable for every data domain.
Let's say I use a certain set of boilerplate fairly regularly:
class Foo {
method abc($a: $b, $c, +#d) is pure {
use Slang::Bar;
…
}
method xyz($a: $b, $c, +#d) is pure {
use Slang::Bar;
…
}
method blarg($a: $b, $c, +#d) is pure {
use Slang::Bar;
…
}
}
I'd rather be able to just say:
class Foo is/does Bar {
bar abc { … }
bar xyz { … }
bar blarg { … }
}
And somewhere in Bar, set up the declaration for bar (or, since class Foo will itself ultimately use its own declarator, it could go somewhere else and doesn't have to be pulled out in a separate Type). How would I go about doing that?
-1. Limitations (only for packages)
The method EXPORTHOW calls .set_how on current $?LANG adding a slang to the latter.
Then it add_package_declarator to the MAIN $?LANG which adds a package_declarator method to its Actions and Grammar. It is, I think, the only "dynamic slang" (in World.nqp).
If what you want is to overwrite routine_declarator. Then you have to write a slang imitating the chain just cited.
If you accept to keep the method keyword and make the automatic signature in the class, say according to the method name, here is a way:
Note: A Package is a container (package, grammar, module, role, knowhow, enum, class, subset). If you put code inside like a method, this gets executed (I'v just tried):
0. Description (EXPORTHOW)
I would use undocumented EXPORTHOW and DECLARE in a module because I did not find a way with Phaser. Apparently it is too late even at BEGIN.
The example I give, is decorating every method in a class (even BUILDALL).
1. Lib (decorator.rakumod)
class DecoratedClassHOW is Metamodel::ClassHOW {
method add_method(Mu $obj, $name, $code_obj) {
sub wrapper ($obj, $a, $b) {
say "Before $name";
my $res = $code_obj($obj, $a, $b);
say "After $name";
return $res;
}
my $res = callwith($obj, $name, &wrapper);
return $res;
}
}
my module EXPORTHOW {
package DECLARE {
constant decorated = DecoratedClassHOW;
}
}
2. Executable
use lib '.';
use decorator-lib;
decorated Foo {
method abc($a, $b) {
say "In abc: $a:$b";
}
}
my $f = Foo.new;
$f.abc(1, 2);
3. Output
Before BUILDALL
After BUILDALL
Before abc
In abc: 1:2
After abc
4. Sources
Grammar::Debugger: exporting a symbol (grammar) overriding find_method and add_method
And npq's NQPTraceHOW::trace-on: Looping with for $_.HOW.method_table($_) creating a new hash overwriting the method cache with the (well-named) nqp::setmethcache.
To automatize the signature, play with .signature
Jnthn article on EXPORTHOW
So post on EXPORTHOW impossible with role
A silly question. I have a oo::class and make an awful thing - including procs into a constructor and a method:
oo::class create MyClass {
constructor {args} {
proc silly {args} {
puts "-------------silly's args: $args"
puts namespace=[namespace current]
set v [namespace current]::sillyvar
puts $v=[incr $v]
puts vars=[info vars [namespace current]::*]
}
}
destructor {
foreach proc {silly SILLY} {
rename $proc ""
}
}
method createprocs {} {
proc SILLY {args} {
puts "-------------SILLY's args: $args"
set v [namespace current]::sillyvar
puts $v=[incr $v]
}
}
method another {} {
silly ;# works ok
}
}
MyClass create myobj
myobj createprocs
[info object namespace myobj]::silly hello world
myobj another
[info object namespace myobj]::SILLY HELLO WORLD
MyClass destroy
The output looks OK:
-------------silly's args: hello world
namespace=::oo::Obj12
::oo::Obj12::sillyvar=1
vars=::oo::Obj12::sillyvar
-------------silly's args:
namespace=::oo::Obj12
::oo::Obj12::sillyvar=2
vars=::oo::Obj12::sillyvar
-------------SILLY's args: HELLO WORLD
::oo::Obj12::sillyvar=3
Can you kind folks say, is it correct? And if so, where it can be useful?
What I see just now is:
1) The silly and the SILLY both are 'exported'.
2) I need no "my silly" and "[self] silly" to access the silly proc from an object.
Can you kind folks say, is it correct?
It looks OK to me, except that you don't need the destructor. The procedures are created in the object instance's namespace (which is also where the object's variables are, FWIW) and that's automatically deleted when the object is destroyed (and vice versa too; you can't have one without the other).
And if so, where it can be useful?
I use procedures (and other commands) created inside methods and constructors a fair bit. They can do things like acting like a private sub-language of Tcl and so on (just as you can do with putting commands in a normal namespace; it's not significantly different), so that's really quite useful. They're also very easy to delegate to using a forwarded method.
One of the easier tricks is to make a sub-object inside an outer object and to delegate some method calls through:
oo::class create Outer {
constructor {} {
Inner create inner [self]
puts "created [self]"
}
destructor {
puts "destroyed [self]"
}
method foo {} {
puts "this is foo of [self]"
}
forward bar inner foo
}
oo::class create Inner {
constructor {outer} {
puts "created [self] inside $outer"
}
destructor {
puts "destroyed [self]"
}
method foo {} {
puts "this is foo of [self]"
}
}
Demonstrating this:
Outer create x
# ⇒ created ::oo::Obj13::inner inside ::x
# ⇒ created ::x
x foo
# ⇒ this is foo of ::x
x bar
# ⇒ this is foo of ::oo::Obj13::inner
x destroy
# ⇒ destroyed ::x
# ⇒ destroyed ::oo::Obj13::inner
This shows the containment and delegation at work (which is based on the same mechanism that your procedures would be using). As you can see, inner is made inside the private namespace of x, and x bar is routed through to inner foo. Even better, when x is destroyed, so is inner with no special code at all; that's very useful in complex code.
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.
I am writing an EDA utility, relying on a TCL 8.6 compliant API. My challenge is as follows: My utility runs on transistors' models in the database, and does some analyisis, using an EDA vendors' TCL API command. I can pass to the TCL commands a TCL procedure name/pointer, and the analysis will rely on my code, rather than the EDA vendors' code. The in-house written proc accepts as an argument a pointer to the specific transistor's instance in the EDA vendor's database.
Now, the EDA vendor allows for TCL 8.6, which means that I want to pass rather than a global proc name, or a namespace proc name, the name/pointer of the specific object's name. How do I do that? In code example:
oo:class create foo {
constructor {} {
variable numy 2
}
method fooshta { mos_pointer } {
puts "now in mosy [get name $mos_pointer ]"
}
destructor {}
}
foo create bar
analyse_tx -proc < how do I refer to bar's method fooshta?>
In a non OOP context, the code would look like:
proc fooshta { mos_pointer } {
puts "now in mosy [get name $mos_pointer ]"
}
analyse_tx -proc fooshta
As can be seen, I am looking for the answer for < how do I refer to bar's method fooshta, so that the EDA tool will invoke it for each transistors' instance? and pass the parameter?>
Thanks.
You can't, not directly, at least not if it is going to be invoked like this:
$procname $thing_to_give_to_your_code
If instead it is invoked like this:
{*}$procname $thing_to_give_to_your_code
Then you can do it by passing in a command prefix.
analyse_tx -proc [list bar fooshta]
This is the one I'd recommend. It might also work if the invoke is done like this:
eval $procname [list $thing_to_give_to_your_code]
This sort of thing is great since it also lets you pass in things like lambda terms bound to apply and so on. It's a very flexible system (since it actually works as a general function currying mechanism) and it's pretty simple.
However, if you're stuck with this style of invoke:
$procname $thing_to_give_to_your_code
then we have to use an indirect mechanism: an intra-interpreter alias will let us make a command (so yes, it will have a name) that delegates to the method:
# The {} are to indicate that this command is aliasing from and to the current interpreter context
interp alias {} delegate-bar-fooshta {} bar fooshta
Then we can just pass delegate-bar-fooshta in as the command name. If you're doing this a lot, you probably ought to put the delegates inside the object's namespace context; it's probably easiest to make a method for setting things up:
oo::class create foo {
constructor {} {
variable numy 2
}
method fooshta { mos_pointer } {
puts "now in mosy [get name $mos_pointer ]"
}
destructor {}
method delegate {method args} {
# We'll also bind in any extra arguments you choose to use
interp alias {} [self namespace]::delegate-$method \
{} [self] $method {*}$args
return [namespace which delegate-$method]
}
}
foo create bar
analyse_tx -proc [bar delegate fooshta]
By doing it like this, killing off the object using the usual mechanisms will also remove the delegate commands that it owns. This is highly convenient in a complex program, as it offloads more of the housekeeping chores to Tcl itself from your script.