118 lines
2.8 KiB
Plaintext
118 lines
2.8 KiB
Plaintext
/*
|
|
This is a tech proposal under construction, please do not use it yet
|
|
for any purpose
|
|
*/
|
|
|
|
/*
|
|
Abstract delegate can be used to proxy read/wrtie field access
|
|
or method call. Default implementation reports error.
|
|
*/
|
|
interface Delegate {
|
|
fun getValue() = Unset
|
|
fun setValue(newValue) { throw NotImplementedException("delegate setter is not implemented") }
|
|
fun invoke(args...) { throw NotImplementedException("delegate setter is not implemented") }
|
|
}
|
|
|
|
/*
|
|
Delegate cam be used to implement a val, var or fun, so there are
|
|
access type enum to distinguish:
|
|
*/
|
|
enum DelegateAccess {
|
|
Val,
|
|
Var,
|
|
Callable
|
|
}
|
|
|
|
// Delegate can be associated by a val/var/fun in a declaraion site using `by` keyword
|
|
|
|
val proxiedVal by proxy(1)
|
|
var proxiedVar by proxy(2, 3)
|
|
fun proxiedFun by proxy()
|
|
|
|
// each proxy is a Lyng expression returning instance of the Proxy interface:
|
|
|
|
/*
|
|
Proxy interface is connecting some named property of a given kind with the `Delegate`.
|
|
It removes the burden of dealing with property name and this ref on each get/set value
|
|
or invoke allowing having one delegate per instance, execution buff.
|
|
*/
|
|
interface Proxy {
|
|
fun getDelegate(propertyName: String,access: DelegateAccess,thisRef: Obj?): Delegate
|
|
}
|
|
|
|
// val, var and fun can be delegated, local or class instance:
|
|
class TestProxy: Proxy {
|
|
override getDelegate(name,access,thisRef) {
|
|
Delegate()
|
|
}
|
|
}
|
|
|
|
val proxy = TestProxy()
|
|
|
|
class Allowed {
|
|
val v1 by proxy
|
|
var v2 by proxy
|
|
fun f1 by proxy
|
|
}
|
|
val v3 by proxy
|
|
var v4 by proxy
|
|
fun f2 by proxy
|
|
|
|
/*
|
|
It means that for example
|
|
Allowed().f1("foo")
|
|
would call a delegate.invoke("foo") on the `Delegate` instance supplied by `proxy`, etc.
|
|
*/
|
|
|
|
// The practic sample: lazy value
|
|
|
|
/*
|
|
The delegate that caches single time evaluated value
|
|
*/
|
|
class LazyDelegate(creator): Delegate {
|
|
private var currentValue=Unset
|
|
|
|
override fun getValue() {
|
|
if( currentValue == Unset )
|
|
currentValue = creator()
|
|
currentValue
|
|
}
|
|
}
|
|
|
|
/*
|
|
The proxy to assign it
|
|
*/
|
|
class LazyProxy(creator) {
|
|
fun getDelegate(name,access,thisRef) {
|
|
if( access != DelegateAccess.Val )
|
|
throw IllegalArgumentException("only lazy val are allowed")
|
|
LazyDelegate(creator)
|
|
}
|
|
}
|
|
|
|
|
|
/*
|
|
A helper function to simplify creation:
|
|
*/
|
|
fun lazy(creator) {
|
|
LazyProxy(creator)
|
|
}
|
|
|
|
// Usage sample and the test:
|
|
var callCounter = 0
|
|
assertEquals(0, clallCounter)
|
|
|
|
val lazyText by lazy { "evaluated text" }
|
|
|
|
// the lazy property is not yet evaluated:
|
|
assertEquals(0, clallCounter)
|
|
// now evaluate it by using it:
|
|
assertEquals("evaluated text", lazyText)
|
|
assertEquals(1, callCounter)
|
|
|
|
// lazy delegate should fail on vars or funs:
|
|
assertThrows { var bad by lazy { "should not happen" } }
|
|
assertThrows { fun bad by lazy { 42 } }
|
|
|
|
|