Last active
December 6, 2016 19:13
-
-
Save gavinking/eb6eea7544d9e9f3acc3b03ebc6d2f1c to your computer and use it in GitHub Desktop.
Using Rank-2 polymorphic generic functions and JavaScript Proxy to create a typesafe proxy API for Ceylon objects
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import ceylon.collection { | |
ArrayList | |
} | |
"Create a typesafe proxy for the given [[instance]], that | |
intercepts method calls and attribute evaluations." | |
Instance proxy<Instance>(Instance instance)(call, get) { | |
"Intercepts method invocations. Delegate to the method | |
of [[target]] by calling `method(args)`." | |
Return call<Return, Args>(Instance target, String name, Args args, Return method(Args args)) | |
given Args satisfies Anything[]; | |
"Intercepts attribute evaluation. The current attribute | |
value for [[target]] is [[attribute]]." | |
Type get<Type>(Instance target, String name, Type attribute); | |
dynamic { | |
dynamic handler = dynamic [ | |
dynamic get(dynamic target, String name, dynamic _) { | |
dynamic field = target[name]; | |
if ("$" in name || name=="constructor") { | |
return field; | |
} | |
if (field is Anything(*Nothing)) { | |
dynamic methodHandler = dynamic [ | |
dynamic apply(dynamic target, dynamic self, dynamic args) { | |
value list = ArrayList<Anything>(); | |
for (a in args) { | |
if (is Anything a) { | |
list.add(a); | |
} | |
} | |
return call<Anything, Anything[]> { | |
target = instance; | |
name = name; | |
args = list.sequence(); | |
method() => target.apply(self, args); | |
}; | |
} | |
]; | |
return Proxy(field, methodHandler); | |
} | |
else { | |
return get<Anything> { | |
target = instance; | |
name = name; | |
attribute = field; | |
}; | |
} | |
} | |
]; | |
return Proxy(instance, handler); | |
} | |
} | |
class Person(shared String name, | |
shared void hello(String name, Integer count) | |
=> print("Hola ``name``! ".repeat(count))) {} | |
shared void run() { | |
value person = Person("Gavin"); | |
Person p = proxy(person) | |
((_, name, args, method) { | |
print("calling '``name``()' with args ``args``"); | |
return method(args); | |
}, | |
(_, name, attribute) { | |
print("got '``name``'"); | |
return attribute; | |
}); | |
print(p.name); | |
p.hello("world", 3); | |
print(p is Person); | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment