Consider the following S4 class:
setClass('Foo', representation(model='data.frame'))
setMethod('initialize', 'Foo',
function(.Object, a, b) {
.Object#model <- data.frame(a, b)
.Object
})
It can be instantiated with:
new('Foo', a=1:4, b=4:7)
So far so good. However, when I try to subclass Foo I get an error.
setClass('Bar', contains='Foo')
>>> Error in data.frame(a, b) : argument "a" is missing, with no default
Personally, I would prefer to instantiate class Foo with explicit arguments because the code is more... well, explicit. However, this does not seem possible, does it? It looks like the signature of initialize must match the slots that the class has, otherwise it's a problem waiting to happen. Am I wrong?
The requirement is that new called with no arguments, new("Foo"), must work. Also, it's probably better practice for your initialize method to take ..., to callNextMethod, and to have arguments after the ... (because initialize is documented to use unnamed arguments for initializing contained classes). So
setMethod(initialize, "Foo", function(.Object, ..., a=integer(), b=integer()) {
callNextMethod(.Object, ..., model=data.frame(a, b))
})
Normally one wants to insulate the user from calling new, and will instead use a constructor Foo. Typically the constructor does whatever coercion you might have instead put in the initialize method, so the initialize method is just not specified.
Foo <- function(a=integer(), b=integer(), ...) {
model <- data.frame(a, b)
new("Foo", model=model, ...)
}
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.
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 can I pass property getter to a function that accepts function type?
Here is an example of what I want achieve:
class Test {
val test: String
get() = "lol"
fun testFun(func: ()->String) {
// invoke it here
}
fun callTest() {
testFun(test::get)
// error: Type mismatch: inferred type is
// KFunction1<#ParameterName Int, Char> but () -> String was expected
}
}
Is there a way?
You can reference the getter by writing ::test (or this::test).
When you write test::get, you are actually referencing the get method on String. That method takes an index and returns the character at that index.
If the property was a var and you want a reference to its setter, you can write ::test::set.
For more info on property references, see here: https://kotlinlang.org/docs/reference/reflection.html#bound-function-and-property-references-since-11
As already mentioned, you can use this::test to refer to the getter. Alternatively, if you have kotlin-reflect, you can do this::test.getter.
When you pass the field as a function, it assumes you mean the getter. As a result, if you want the setter, you have two options:
this::test::set
or
this::test.setter
The latter, just like this::test.getter requires kotlin-reflect, or the program will crash (tested locally with Kotlin 1.2.50)
You can, however, get the getter in another way. But I recommend you just stick with this::test because it's shorter.
You can do:
this::something::get
With just something::get it refers to the method inside the String class, which returns a char at an index. For reference, the method declaration:
public override fun get(index: Int): Char
If you don't mind, just use { test } (e.g. testFun { test }). This will exactly translate to your () -> String. The next best alternative is probably ::test (or this::test) as was already mentioned.
The second has probably only minor (negligible?) impact on performance. I did not test it myself, nor did I found any source which tells something regarding it. The reason why I say this, is how the byte code underneath looks like. Just due to this question I asked myself about the difference of the two: Is the property reference (::test) equivalent to a function accessing the property ({ test }) when passed as argument e.g. `() -> String`?
It seems that you are doing something wrong on logical level.
If you are overriding get method of a variable, then you can access it's value through this get method. Thus, why bother with test::get (which is totally different method, by the way, all you are doing is trying to access char from string), when you can just access variable by it's name?
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!
I have a class hierarchy with the superclass fb of which no objects should exist (I tried virtual classes but ran in the problem that you can not initialize objects from virtual classes). Further, I have two sub classes (foo, bar) with the same slots. Now I want to make a new object, using an initialize method to the superclass that returns an object of one of the subclasses based on some value:
setClass("fb", representation( x = "numeric"))
setClass("foo", contains = "fb")
setClass("bar", contains = "fb")
setMethod("initialize", "fb", function(.Object, x) {
if (x < 5) class(.Object) <- "foo"
else class(.Object) <- "bar"
.Object#x <- x
.Object
})
> new("fb", x = 3)
Error in initialize(value, ...) :
initialize method returned an object of class "foo" instead of the required class "fb"
Obviously (and probably with good reasons) R disallows that. Is there a way to achieve what I want within a method and not using an if-else construct when I create the new object?
S4 helps us to color within the lines. So your fb class should be virtual, your initialize method shouldn't change the class of .Object. You might write a function fb that does your conditional instantiation.
setClass("fb", representation( x = "numeric", "VIRTUAL"))
setClass("foo", contains = "fb")
setClass("bar", contains = "fb")
fb <-
function(x)
{
if (x < 5) new("foo", x=x)
else new("bar", x=x)
}
fb is a constructor, more convenient for the user, and separates the interface to your class hierarchy from its implementation which is generally considered a good thing.
And for what it's worth an implicit constraint on S4 initialize methods is that new("foo") (invoking new with the class name but no additional arguments) must work (otherwise there are failures when you try to extend foo). So the paradigm for an initialize method is along the lines of
setMethod(initialize, "foo", function(.Object, ..., x=1) {
.Object <- callNextMethod(.Object, ...)
.Object#x <- x
.Object
})
though often (as in this case, where initialize is just doing slot assignment) there is no need for an initialize method at all. Note the use of ..., the positioning of x (requiring that the argument be named in the corresponding call to new) and the use of a default value.