How to add a default method for an action class of a grammar? - grammar

Recently, I experimented with some grammars for modifying small parts of a
file. In those test cases, I would keep much
of the file as it was, only modifying small pieces here and there, see
this review question for an example.
So I needed one (or a few) action methods (i.e. methods in the action class of the
grammar) where I would attach the modified parts of the file to the
match object using its
make method. The problem was that the grammar itself would have many more
token/rules/regex with complicated
nesting. Hence, there was a need to propagate (by successively calling
make()) the small change (currently attached to
a token's match object) up to the TOP() method in the action class
such that everything
else (all other tokens/rules/regexes) in the file was kept untouched
in the result returned from the grammar's .parse() call.
So all methods in the action class except for one, was on the exact same form:
method make-data ($match-data) {
$match-data.make( [~] $match-data.chunks.map: {$_.value.?made // $_.value} );
}
Now, this explicit repetition of the same code for all action methods seems
to me very verbose, and also breaks with the DRY programming principle.
Is there a way to tell the grammar class that if an action method
(corresponding to a token in the grammar) is
not specified , it will default to the make-data method above (or a similar one)?
So in this case, I envision a DEFAULT() method in the action class:
method DEFAULT ($match-data) {
$match-data.make( [~] $match-data.chunks.map: {$_.value.?made // $_.value} );
}
that is called if a token in the grammar class does not have a
corresponding method in the action class.

Perl 6's Type system will call a FALLBACK method, if it's present in a class and an unknown method call is made.
The following solution adds default construction methods to the Calculations actions class.
grammar Calculator {
token TOP { [ <add> | <sub> ] }
rule add { <num> '+' <num> }
rule sub { <num> '-' <num> }
token num { \d+ }
}
class Calculations {
method ws($) {}
method FALLBACK($token, $match-data) {
$match-data.make( [~] $match-data.chunks.map: {
$_.value.?made // $_.value;
} );
}
}
say Calculator.parse('2 + 3', actions => Calculations).made;

Related

Why does `BUILD` not see attribute from parent class?

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.

How to avoid if..else(or any conditionals) while deciding which method to be called?

How to follow Open Close Principle without violating LSP while deciding which method to be invoked with different parameters in a statically typed language?
Consider the requirement like
Action 1: perform DB operation on Table 1
Action 2: Perform DB operation on Table 2 based on input
Action 3: Do Nothing
Code for above requirement would look like
process(obj) {
if(obj.type === action1) {
db.updateTable1()
}
if(obj.type === action2) {
db.updateTable2(obj.status)
}
if(obj.type === action3) {
//May be log action 3 recieved
}
}
Figured out a way to follow OCP in above code for additional actions, by moving body of if statement to method and maintain a map of keys with action as name. Reference
However feels solution is violating the OCP as method wrapping the contents of first if block will not receive any parameter, second method wrapping the contents of second if block will have a parameter.
Either it forces all method to follow the same signature in trade off following OCP but violating LSP or give up OCP itself and thereby live with multi if statements.
A simple solution would be to define a strategy, which execute the code currently contained in the if / else if / else branches:
interface Strategy {
String getType();
void apply();
}
The strategies need to be registered:
class Executor {
private Map<String, Strategy> strategies;
void registerStrategy(strategy Strategy) {
strategies.put(strategy.getType(), strategy);
}
void process(obj) {
if (strategies.containsKey(obj.type)) {
// apply might execute db.updateTable1(),
// depending on the interface's implementation
strategies.get(obj.type).apply();
} else {
System.out.println("No strategy registered for type: " + obj.type);
}
}
}
The tradeoffs you recognise are unfortunately what you'll have to deal with when working with OOP in Java, C++, C# etc as the systems are dynamically put together and SOLID is kind of addresses the flaws. But the SOLID principles are intended to provide guidance, I wouldn't follow them idiomatically.
I hoped to find an example by better programmers than myself illustrating the command pattern. But I was just finding really bad examples which were not really addressing your question.
The problem of defining an associating an intent (defined as string or enum, a button click) with an action (an object, a lambda function) will always require a level of indirection we have to deal with. Some layers of abstractions are acceptable, for example: never call a model or service directly in a view. You could also think of implementing am event dispatcher and corresponding listeners, which would help with the loose coupling. But at some lower level you'll have to look up all listeners ...
The nature of obj is ambiguous, but I would recommend having a well-defined interface and pass it throughout your code where the class implementation of your interface would be equivalent to your 'action'. Here's an example of what that might look like in Typescript:
interface someDBInterface {
performAction() : void;
}
function process(obj : someDBInterface) {
let result = obj.performAction();
}
class action1 implements someDBInterface {
status: any
performAction() {
//db.updateTable1();
}
}
class action2 implements someDBInterface {
status : any
performAction() {
//db.updateTable1(this.status);
}
}
class action3 implements someDBInterface {
performAction() {
//May be log action 3 recieved
}
}
If this doesn't meet your requirements, feel free to reach out :)

Making a custom declarator

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

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'))
}
}

How can I return context sensitive return values in Perl 6?

In the summary of differences between Perl 5 and Perl 6, it is noted that the wantarray function is gone:
wantarray() is gone
wantarray is gone. In Perl 6, context
flows outwards, which means that a
routine does not know which context it
is in.
Instead you should return objects that
do the right thing in every context.
Could someone provide an example of how such an object is created?
I think 2 examples might be:
http://perlcabal.org/syn/S13.html#Type_Casting
A class may define methods that allow it to respond as if it were a routine, array, or hash. The long forms are as follows:
method postcircumfix:<( )> ($capture) {...}
method postcircumfix:<[ ]> (**#slice) {...}
method postcircumfix:<{ }> (**#slice) {...}
Those are a bit unwieldy, so you may also use these short forms:
method &.( $capture ) {...}
method #.[ **#slice ] {...}
method %.{ **#slice } {...}
Also, I think this might be relevant though less so: http://perlcabal.org/syn/S12.html
Search for:
You may write your own accessors to override any or all of the autogenerated ones.
So you return an object which has several context-specific accessors.
Interestingly enough, it started out with Perl6 replacing "wantarray" with a generic "want": RFC 98 (v1) context-based method overloading, circa 2000, also at http://dev.perl.org/perl6/rfc/21.html . I'm not sure why/when the change was made.
This comment on Reddit about the blog post Immutable Sigils and Context gives the following examples:
class GeoLocation is Array {
method Str { 'middle of nowhere' }
}
sub remote_location {
return GeoLocation.new(1e6 xx 3);
}
# or even easier:
sub remote_location {
return (1e6 xx 3) but 'middle of nowhere';
}