Using captured types to type a class's attributes - metaprogramming

I've followed the directions from the answers to the SO question
How can classes be made parametric in Perl 6?. However, I've hit some soft roadblock; I'm trying to type an internal class's attribute using the type capture and getting the following error:
Died with X::TypeCheck::Assignment
in submethod BUILDALL at ...
in method insert at ...
in block <unit> at ...
In the following example, I've typed the class BinaryNode's $.item attribute (with T) but doing so causes the above error:
class BinarySearchTree {
my role BTSImpl[::T] {
my class BinaryNode is rw {
has T $.item;
has BinaryNode $.left;
has BinaryNode $.right;
}
method create-node( T $x ) {
BinaryNode.new(item => $x)
}
}
method ^parameterize(Mu:U \this, Mu \T) {
my $type := this.^mixin: BTSImpl[T];
$type.^set_name: this.^name ~ '[' ~ T.^name ~ ']';
$type
}
}
my $bst = BinarySearchTree[Int].new;
$bst.create-node(6);

First off, there's almost no need to perform the class + ^parameterize + role trick. It appears in some of the internals because it helps deal with some bootstrapping problems (the kind of fun one has when defining a language in terms of itself). However, in normal Raku code, just write a parametric role instead of a class. From the point of view of the consumer, there's usually no difference; one can:
Call .new on it to make an instance (which actually creates a class, known as a "pun", behind the scenes and makes the instance of that)
In fact, call any method on the type object with the same result; new isn't special
Inherit from it (again, it works on the automatically produced class)
With the added bonus that somebody can also compose it instead of inheriting.
Secondly, there's no relationship between a class defined inside of a role and the enclosing role (this is a general principle: nesting of one package inside of another doesn't imply any relationship between them at an object model level). Thus we need to make that separately generic and instantiate it.
These two get us to:
role BinarySearchTree[::T] {
my role BinaryNode[::T] is rw {
has T $.item;
has BinaryNode $.left;
has BinaryNode $.right;
}
method create-node( T $x ) {
BinaryNode[T].new(item => $x)
}
}
my $bst = BinarySearchTree[Int].new;
$bst.create-node(6);
Which really should work, but the compiler seems to get the timing wrong on the BinaryNode[T]. We can work around that by just forcing it to delay the paramerterization until runtime; there's many ways we may do that, but writing BinaryNode[$(T)] compact and cheap (optimizes into pretty much no extra cost). Thus giving a working solution:
role BinarySearchTree[::T] {
my role BinaryNode[::T] is rw {
has T $.item;
has BinaryNode $.left;
has BinaryNode $.right;
}
method create-node( T $x ) {
BinaryNode[$(T)].new(item => $x)
}
}
my $bst = BinarySearchTree[Int].new;
$bst.create-node(6);

Related

How do you access private methods or attributes from outside the type they belong to?

In some rare cases where this would actually be acceptable, like in unit tests, you may want to get or set the value of a private attribute, or call a private method of a type where it shouldn't be possible. Is it really impossible? If not, how can you do it?
There are two ways you can access a private method of a type, and one way to get private attributes. All require meta-programming except for the first way to invoke private methods, whose explanation still involves meta-programming anyway.
As an example, we will be implementing a Hidden class that hides a value using a private attribute, and a Password class that uses Hidden to store a password. Do not copy this example to your own code. This is not how you would reasonably handle passwords; this is solely for example's sake.
Calling private methods
Trusting other classes
Metamodel::Trusting is the meta-role that implements the behaviour needed for higher-order workings (types of types, or kinds, referred to from hereon out as HOWs) to be able to trust other types. Metamodel::ClassHOW (classes, and by extension, grammars) is the only HOW that's builtin to Rakudo that does this role.
trusts is a keyword that can be used from within packages to permit another package to call its private methods (this does not include private attributes). For example, a rough implementation of a password container class could look like this using trusts:
class Password { ... }
class Hidden {
trusts Password;
has $!value;
submethod BUILD(Hidden:D: :$!value) {}
method new(Hidden:_: $value) {
self.bless: :$value
}
method !dump(Hidden:D: --> Str:D) {
$!value.perl
}
}
class Password {
has Hidden:_ $!value;
submethod BUILD(Password:D: Hidden:D :$!value) {}
method new(Password:_: Str:D $password) {
my Hidden:D $value .= new: $password;
self.bless: :$value
}
method !dump(Password:D: --> Str:D) {
qc:to/END/;
{self.^name}:
$!value: {$!value!Hidden::dump}
END
}
method say(Password:D: --> Nil) {
say self!dump;
}
}
my Password $insecure .= new: 'qwerty';
$insecure.say;
# OUTPUT:
# Password:
# $!value: "qwerty"
#
Using the ^find_private_method meta-method
Metamodel::PrivateMethodContainer is a meta-role that implements the behaviour for HOWs that should be able to contain private methods. Metamodel::MethodContainer and Metamodel::MultiMethodContainer are the other meta-roles that implement the behaviour for methods, but those won't be discussed here. Metamodel::ClassHOW (classes, and by extension, grammars), Metamodel::ParametricRoleHOW and Metamodel::ConcreteRoleHOW (roles), and Metamodel::EnumHOW (enums) are the HOWs builtin to Rakudo that do this role. One of Metamodel::PrivateMethodContainer's methods is find_private_method, which takes an object and a method name as parameters and either returns Mu when none is found, or the Method instance representing the method you're looking up.
The password example can be rewritten not to use the trusts keyword by removing the line that makes Hidden trust Password and changing Password!dump to this:
method !dump(Password:D: --> Str:D) {
my Method:D $dump = $!value.^find_private_method: 'dump';
qc:to/END/;
{self.^name}:
$!value: {$dump($!value)}
END
}
Getting and setting private attributes
Metamodel::AttributeContainer is the meta-role that implements the behaviour for types that should contain attributes. Unlike with methods, this is the only meta-role needed to handle all types of attributes. Of the HOWs builtin to Rakudo, Metamodel::ClassHOW (classes, and by extension, grammars), Metamodel::ParametricRoleHOW and Metamodel::ConcreteRoleHOW (roles), Metamodel::EnumHOW (enums), and Metamodel::DefiniteHOW (used internally as the value self is bound to in accessor methods for public attributes) do this role.
One of the meta-methods Metamodel::AttributeContainer adds to a HOW is get_attribute_for_usage, which given an object and an attribute name, throws if no attribute is found, otherwise returns the Attribute instance representing the attribute you're looking up.
Attribute is how attributes are stored internally by Rakudo. The two methods of Attribute we care about here are get_value, which takes an object that contains the Attribute instance and returns its value, and set_value, which takes an object that contains the Attribute instance and a value, and sets its value.
The password example can be rewritten so Hidden doesn't implement a dump private method like so:
class Hidden {
has $!value;
submethod BUILD(Hidden:D: :$!value) {}
method new(Hidden:_: $value) {
self.bless: :$value;
}
}
class Password {
has Hidden:_ $!value;
submethod BUILD(Password:D: Hidden:D :$!value) {}
method new(Password:_: Str:D $password) {
my Hidden:D $value .= new: $password;
self.bless: :$value
}
method !dump(Password:D: --> Str:D) {
my Attribute:D $value-attr = $!value.^get_attribute_for_usage: '$!value';
my Str:D $password = $value-attr.get_value: $!value;
qc:to/END/;
{self.^name}:
$!value: {$password.perl}
END
}
method say(Password:D: --> Nil) {
say self!dump;
}
}
my Password:D $secure .= new: 'APrettyLongPhrase,DifficultToCrack';
$secure.say;
# OUTPUT:
# Password:
# $!value: "APrettyLongPhrase,DifficultToCrack"
#
F.A.Q.
What does { ... } do?
This stubs a package, allowing you to declare it before you actually define it.
What does qc:to/END/ do?
You've probably seen q:to/END/ before, which allows you to write a multiline string. Adding c before :to allows closures to be embedded in the string.
Why are grammars classes by extension?
Grammars use Metamodel::GrammarHOW, which is a subclass of Metamodel::ClassHOW.
You say ^find_private_method and ^get_attribute_for_usage take an object as their first parameter, but you omit it in the example. Why?
Calling a meta-method on an object passes itself as the first parameter implicitly. If we were calling them directly on the object's HOW, we would be passing the object as the first parameter.

TS: Cannot invoke an expression whose type lacks a call signature when defined dynamically, but it works

I'm still quite new to typescript, so please be gentle with me if I'm doing something with no sense for this technology!
The problem that I'm trying to solve is having a dynamic way to define how my application errors should be structured, but leaving to the users the faculty to enrich the messages.
So I tried to create this logic in a module that could be extended easily from the application, but I'm currently facing the problem:
Error:(35, 18) TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'ErrorMessage' has no compatible call signatures.
What I thought it was a good idea (but please tell me if I'm wrong), was to use a register and a map to have the possibility to extend this mapping every time I want. So I created my ErrorMessage interface to be like the following:
export interface ErrorMessage {
actionMessage: string;
actionSubject: string;
originalErrorMessage?: string;
toString: () => string;
}
and a register for these, called ErrorResponseRegister, as it follows:
export enum defaultErrors {
ExceptionA = 'ExceptionA',
ExceptionB = 'ExceptionB',
}
export class ErrorResponseRegister {
private mapping: Map<string, ErrorMessage>;
constructor() {
this.mapping = new Map()
.set(defaultErrors.ExceptionA, exceptionAErrorMessage)
.set(defaultErrors.ExceptionB, exceptionBErrorMessage);
}
}
So at the end, every ErrorMessage function should look like:
export function exceptionAErrorMessage(originalErrorMessage?: string): ErrorMessage {
return {
enrichment1: "Something happened",
enrichment2: "in the application core",
originalErrorMessage: originalErrorMessage,
toString(): string {
return `${this.enrichment1} ${this.enrichment2}. Original error message: ${originalErrorMessage}`;
},
};
}
Please note I haven't used classes for this ones, as it doesn't really need to be instantiated
and I can have a bunch of them where the toString() method can vary. I just want to enforce the errors should have an enrichment1 and enrichment2 that highlight the problem in a better way for not-technical people.
So, now, back to code. When I'm trying to use the exceptionAErrorMessage statically, I can't see any problem:
console.log(exceptionAErrorMessage(originalErrorMessage).toString())
But when I try dynamically, using the map defined in the ErrorResponseRegister, something weird happens:
// In ErrorResponseRegister
public buildFor(errorType: string, originalErrorMessage?: string): Error {
const errorMessageBuilder = this.mapping.get(errorType);
if (errorMessageBuilder) {
return errorMessageBuilder(originalErrorMessage).toString();
}
return "undefined - do something else";
}
The code works as expected, the error returned is in the right format, so the toString function is executed correctly.
BUT, the following error appears in the IDE:
Error:(32, 18) TS2349: Cannot invoke an expression whose type lacks a call signature. Type 'ErrorMessage' has no compatible call signatures.
The line that causes the problem is
errorMessageBuilder(originalPosErrorMessage).toString()
Can someone help me to understand what I'm doing wrong?
It looks like your problem is you've mistyped mapping... it doesn't hold ErrorMessage values; it holds (x?: string)=>ErrorMessage values:
private mapping: Map<string, (x?: string) => ErrorMessage>;
What's unfortunate is that you initialize this variable via new Map().set(...) instead of the using an iterable constructor argument.
The former returns a Map<any, any> which is trivially assignable to mapping despite the mistyping. That is, you ran smack into this known issue where the standard library's typings for the no-argument Map constructor signature produces Map<any, any> which suppresses all kinds of otherwise useful error messages. Perhaps that will be fixed one day, but for now I'd suggest instead that you use the iterable constructor argument, whose type signature declaration will infer reasonable types for the keys/values:
constructor() {
this.mapping = new Map([
[defaultErrors.ExceptionA, exceptionAErrorMessage],
[defaultErrors.ExceptionB, exceptionBErrorMessage]
]); // inferred as Map<defaultErrors, (orig?: string)=>ErrorMessage>
}
If you had done so, it would have flagged the assignment as an error with your original typing for mapping (e.g., Type 'Map<defaultErrors, (originalErrorMessage?: string | undefined) => ErrorMessage>' is not assignable to type 'Map<string, ErrorMessage>'.) Oh well!
Once you make those changes, things should behave more reasonably for you. Hope that helps; good luck!
Link to code

Groovy DSL given syntax validation

Actually I'm experimenting writing a DSL with groovy. So far ...
There are some things unclear to be regarding delegation and intercepting unwanted (Closure) structures:
first of all: How can I throw a (type of?) Exception to point to the correct line of code in the DSL that fails?
assuming
abstract MyScript extends Script {
def type(#DelegateTo(MyType) Closure cl) {
cl.delegate = new MyType()
cl()
this
}
}
under
new GroovyShell(this.class.classLoader, new CompilerConfiguration(scriptBaseClass: MyScript.name)).evaluate(…)
the passed DSL / closure
type {
foo: "bar"
}
passes silently.
I'm aware of, that foo: is just a POJ label but I'm not that sure what that defined Closure is interpreted as?
Neither did I found anything regarding the AST metaprogramming to get in touch of any defined labels to use them?
giving in
type {
foo = "bar"
}
it's clear that he will try to set the property foo, but do I really have to intercept unwanted fields/props by
class MyType {
def propertyMissing(String name) {
… // where I'm unable to println name since this leads to field access 'name' ...
}
}
while user is still allowed to pass
type {
foo "bar"
}
which leads to method not defined .. so I have to write additionally some metaClass.methodMissing or metaClass.invokeMethod stuff ..
meanwhile I tend to dismiss any closures in my dsl only working with simple
def type(Map vars) {
store << new MyType(vars)
// where in the constructor I was forced to write metaClass stuff to validate that only fields are given in the map that are defined in the class
}
that works, but both drafts are not what I expected to do when reading "groovy is so great for making DSLs" ...
I would experiment with the different options and then settle for one.
To guide your users you should give feedback similar to that of the regular compiler (i.e. line-number and column, maybe the expression).
Enforcing the correctness of the input can be non-trivial -- depending on your DSL.
For example:
type {
foo: "bar"
}
Is just a closure that returns the String bar. Is that something your user is supposed to do? The label will be part of the AST, AFAIK in org.codehaus.groovy.ast.stmt.Statement.statementLabels. If you want this syntax to assign something to foo then you'll need to rewrite the AST. The Expression could become a Declaration for the local Variable foo or could become an assignment for the Field foo. That's really up to you, however, Groovy gives you some capabilities that make creating a DSL easier:
You already used #DelegateTo(MyType) so you could just add a Field foo to MyType:
class MyType {
String foo
}
And then either use #CompileStatic or #TypeChecked to verify your script. Note that #CompileStatic will deactivate Run-time Metaprogramming (i.e. propertyMissing etc. won't be called anymore.) while #TypeChecked does not. This, however, will only verify Type-Correctness. That is: assigning to anything but a declared Field will fail and assigning an incompatible Type will fail. It does not verify that something has been assigned to foo at all. If this is required you can verify the contents of the delegate after calling the Closure.

dart method calling context

I used the below to see how dart calls methods passed in to other methods to see what context the passed in method would/can be called under.
void main() {
var one = new IDable(1);
var two = new IDable(2);
print('one ${caller(one.getMyId)}'); //one 1
print('two ${caller(two.getMyId)}'); //two 2
print('one ${callerJustForThree(one.getMyId)}'); //NoSuchMethod Exception
}
class IDable{
int id;
IDable(this.id);
int getMyId(){
return id;
}
}
caller(fn){
return fn();
}
callerJustForThree(fn){
var three = new IDable(3);
three.fn();
}
So how does caller manager to call its argument fn without a context i.e. one.fn(), and why does callerJustForThree fail to call a passed in fn on an object which has that function defined for it?
In Dart there is a difference between an instance-method, declared as part of a class, and other functions (like closures and static functions).
Instance methods are the only ones (except for constructors) that can access this. Conceptually they are part of the class description and not the object. That is, when you do a method call o.foo() Dart first extracts the class-type of o. Then it searches for foo in the class description (recursively going through the super classes, if necessary). Finally it applies the found method with this set to o.
In addition to being able to invoke methods on objects (o.foo()) it is also possible to get a bound closure: o.foo (without the parenthesis for the invocation). However, and this is crucial, this form is just syntactic sugar for (<args>) => o.foo(<args>). That is, this just creates a fresh closure that captures o and redirects calls to it to the instance method.
This whole setup has several important consequences:
You can tear off instance methods and get a bound closure. The result of o.foo is automatically bound to o. No need to bind it yourself (but also no way to bind it to a different instance). This is way, in your example, one.getMyId works. You are actually getting the following closure: () => one.getMyId() instead.
It is not possible to add or remove methods to objects. You would need to change the class description and this is something that is (intentionally) not supported.
var f = o.foo; implies that you get a fresh closure all the time. This means that you cannot use this bound closure as a key in a hashtable. For example, register(o.foo) followed by unregister(o.foo) will most likely not work, because each o.foo will be different. You can easily see this by trying print(o.foo == o.foo).
You cannot transfer methods from one object to another. However you try to access instance methods, they will always be bound.
Looking at your examples:
print('one ${caller(one.getMyId)}'); //one 1
print('two ${caller(two.getMyId)}'); //two 2
print('one ${callerJustForThree(one.getMyId)}'); //NoSuchMethod Exception
These lines are equivalent to:
print('one ${caller(() => one.getMyId())}');
print('two ${caller(() => two.getMyId())}');
print('one ${callerJustForThree(() => one.getMyId())}';
Inside callerJustForThree:
callerJustForThree(fn){
var three = new IDable(3);
three.fn();
}
The given argument fn is completely ignored. When doing three.fn() in the last line Dart will find the class description of three (which is IDable) and then search for fn in it. Since it doesn't find one it will call the noSuchMethod fallback. The fn argument is ignored.
If you want to call an instance member depending on some argument you could rewrite the last example as follows:
main() {
...
callerJustForThree((o) => o.getMyId());
}
callerJustForThree(invokeIDableMember){
var three = new IDable(3);
invokeIDableMember(three);
}
I'll try to explain, which is not necessarily a strength of mine. If something I wrote isn't understandable, feel free to give me a shout.
Think of methods as normal objects, like every other variable, too.
When you call caller(one.getMyId), you aren't really passing a reference to the method of the class definition - you pass the method "object" specific for instance one.
In callerJustForThree, you pass the same method "object" of instance one. But you don't call it. Instead of calling the object fn in the scope if your method, you are calling the object fn of the instance three, which doesn't exist, because you didn't define it in the class.
Consider this code, using normal variables:
void main() {
var one = new IDable(1);
var two = new IDable(2);
caller(one.id);
caller(two.id);
callerJustForThree(one.id);
}
class IDable{
int id;
IDable(this.id);
}
caller(param){
print(param);
}
callerJustForThree(param){
var three = new IDable(3);
print(three.id); // This works
print(param); // This works, too
print(three.param); // But why should this work?
}
It's exactly the same concept. Think of your callbacks as normal variables, and everything makes sense. At least I hope so, if I explained it good enough.

Why missingMethod is not working for Closure?

UPDATE
I have to apologize for confusing the readers. After I got totally lost in the code, I reverted all my changes from Mercurial repo, carefully applied the same logic as before -- and it worked. The answers below helped me understand the (new to me) concept better, and for that I gave them upvotes.
Bottom line: if a call to a missing method happens within a closure, and resolution set to DELEGATE_FIRST, methodMissing() will be called on the delegate. If it doesn't -- check you own code, there is a typo somewhere.
Thanks a lot!
Edit:
OK, now that you've clarified what your are doing (somewhat ;--))
Another approach (one that I use for DSLs) is to parse your closure group to map via a ClosureToMap utility like this:
// converts given closure to map method => value pairs (1-d, if you need nested, ask)
class ClosureToMap {
Map map = [:]
ClosureToMap(Closure c) {
c.delegate = this
c.resolveStrategy = Closure.DELEGATE_FIRST
c.each{"$it"()}
}
def methodMissing(String name, args) {
if(!args.size()) return
map[name] = args[0]
}
def propertyMissing(String name) { name }
}
// Pass your closure to the utility and access the generated map
Map map = new ClosureToMap(your-closure-here)?.map
Now you can iterate through the map, perhaps adding methods to applicable MCL instance. For example, some of my domains have dynamic finders like:
def finders = {
userStatusPaid = { Boolean active = true->
eq {
active "$active"
paid true
}
}
}
I create a map using the ClosureToMap utility, and then iterate through, adding map keys (methods, like "userStatus") and values (in this case, closure "eq") to domain instance MCL, delegating the closure to our ORM, like so:
def injectFinders(Object instance) {
if(instance.hasProperty('finders')) {
Map m = ClosureToMap.new(instance.finders).map
m?.each{ String method, Closure cl->
cl.delegate = instance.orm
cl.resolveStrategy = Closure.DELEGATE_FIRST
instance.orm.metaClass."$method" = cl
}
}
}
In this way in controller scope I can do, say:
def actives = Orders.userStatusPaid()
and "eq" closure will delegate to the ORM and not domain Orders where an MME would occur.
Play around with it, hopefully I've given you some ideas for how to solve the problem. In Groovy, if you can't do it one way, try another ;--)
Good luck!
Original:
Your missingMethod is defined on string metaclass; in order for it to be invoked, you need "someString".foo()
If you simply call foo() by itself within your closure it will fail, regardless of delegation strategy used; i.e. if you don't use the (String) delegate, good luck. Case in point, do "".foo() and it works.
I don't fully understand the issue either, why will you not have access to the closure's delegate? You are setting the closure's delegate and will invoke the closure, which means you will have access to the delegate within the closure itself (and can just delegate.foo())
nope, you will not catch a missing method and redirect it to the delegate with metaclass magic.
the closure delegate is the chance to capture those calls and adapt them to the backing domain.
that means...
you should create your own delegate with the methods required by the dsl.
do not try to force a class to do delegate work if it's not designed for the task, or the code will get really messy in not time.
keep everything dsl related in a set of specially designed delegate classes and everything will suddenly become ridiculously simple and clear.

Resources