/* 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 } }