Wrong "this" being used in nested closures - kotlin

I'm trying to keep this minimal, but let me know if I'm being too minimal.
Suppose you have a class hierarchy like this, designed for generating HTML (inspired by the Kotlin tutorial; semi-pseudocode follows):
class Tag {
protected val children = arrayListOf<Tag>()
operator fun String.unaryPlus() = children.add(Text(this))
}
class TagWithChildren : Tag() {
fun head(init: Head.() -> Unit) = initializeTag(Head(), init)
fun script(init: Script.() -> Unit) = initializeTag(Script(), init)
fun <T : Tag> initializeTag(tag: T, init: T.() -> Unit): T {
tag.init()
children.add(tag)
return tag
}
}
class Head : TagWithChildren()
class Script : Tag()
class Text(val str: Text) : Tag()
Notice that Head has head and script methods while Script doesn't.
Now you can construct a template that looks like this:
head {
script {
+"alert('hi');"
}
}
Which works great! However, if the block passed to script tries to call methods that aren't available on Script, it can call the method on Head instead. For example,
head {
script {
script {
+"alert('hi');"
}
}
}
not only isn't a compile error, it's actually equivalent to
head {
script {
}
script {
+"alert('hi');"
}
}
which is super confusing, from a template author's perspective.
Is there any way to prevent method lookups from traveling up the scope like that? I only want it to look at the innermost scope.
UPDATE 11/24/2016:
Kotlin 1.1-M03 has introduced scope control, which I believe solves exactly this problem. https://blog.jetbrains.com/kotlin/2016/11/kotlin-1-1-m03-is-here/

The current behavior is intentional. Code in a lambda has access to receivers of all enclosing scopes. It is possible that a future version of Kotlin will add a modifier that will restrict a lambda with receiver to calling methods on that receiver only and not the enclosing scopes, but in the current version there's no way to change that behavior.

As a workaround, I can have it fail at runtime if I change the classes to look like this:
open class Tag {
operator fun String.unaryPlus()
// pulled up from TagWithChildren, call protected method
fun head(init: Head.() -> Unit) = addChild(Head())
fun script(init: Script.() -> Unit) = addChild(Head())
// throws in Tag
open protected fun addChild(t: Tag) = throw IllegalArgumentException()
}
class TagWithChildren : Tag() {
// overridden to not throw in subclass
protected override fun addChild(t: Tag) = children.add(t)
}
This way, every Tag has the builder methods (solving the scoping problem), but actually calling them may result in a runtime failure.

Related

Compose functions with different context receivers

I started playing with the new context receivers feature. I intend to use that as a "localized dependency injection" to pass client context around. Currently, I have this (ClientProvider is a fun interface):
fun <T> withClient(client: Client, block: ClientProvider.() -> T) =
ClientProvider { client }.block()
This works pretty well in the production code, e.g. I can do
class MyService {
context(ClientProvider)
fun methodUsingClient() {}
}
However, an issue arises when I want to mockk this code in tests. Currently, I have
val myService: MyService = mockk { every { methodUsingClient() } just runs }
This obviously doesn't work because the ClientProvider context is missing. I would like to fix that by composing mockk and withClient. I imagine it could look like this
inline fun <reified T : Any> mockkWithClient(noinline block: context(T) ClientProvider.() -> Unit) =
mockk<T> { withClient(mockk(), block) }
This indeed works at the call site, i.e., the compiler seems to be happy with
val myService: MyService = mockkWithClient { every { methodUsingClient() } just runs }
but the function itself doesn't compile - the compiler complains about the block parameter:
Type mismatch.
Required:
ClientProvider.() → TypeVariable(T)
Found:
context(T) ClientProvider.() → Unit
Intuitively, I would expect that the extra T context wouldn't harm the block usage inside withClient but apparently, it does.
Is there any way how I can define mockkWithClient so it can be used as outlined above?
I got a useful answer to my other, more generally formulated question, and based on that I was able to solve this problem as well (the key point I was missing is that I have to manually pass the receivers to the block):
inline fun <reified T : Any> mockkWithClient(noinline block: context(ClientProvider) T.() -> Unit) =
withMockClient<T> { mockk { block(this#withMockClient, this#mockk) } }
fun <T> withMockClient(block: ClientProvider.() -> T) =
ClientProvider { mockk() }.block()
The intended usage is then as expected:
mockkWithClient { every { methodUsingClient() }
Note, however, the very specific type of block: it's context(ClientProvider) T.() -> Unit. If I read the documentation correctly I should be also able to write context(ClientProvider, T) () -> Unit but that doesn't compile with the message Subtyping relation between context receivers is prohibited. The root cause of this is still unknown to me but my original problem is solved, nevertheless.

Is there a way to get Kotlin function's owner via reflection

Suppose we have the following functions:
class MyClass {
fun myFunction() {}
companion object {
fun myStaticFunction() {}
}
}
fun myTopLevelFunction() {}
When I am debugging thru the following:
val functions = listOf(
MyClass::myFunction,
MyClass.Companion::myStaticFunction,
::myTopLevelFunction
)
functions.forEach {
val names = (it::class.java).fields.map {
method -> println(method.name)
}
Intellij can correctly show me in "Evaluate Expression" this information
owner (MyClass, MyClass.Companion etc.
whether this is a top level function
But I cannot find a way to do it via reflection. For instance, I cannot figure out how to read function's owner. What am I missing? TIA.
In Intellij I can see functions' owners like this
myFunction -> MyClass
myStaticFunction -> MyClass.Companion
myTopLevelFunction -> MyFileNameKt
I believe KFunction does not provide us with such capability. If you are not afraid of using publicly available utils from kotlin.jvm.internal package then you can check if KFunction is a CallableReference and then use its owner property:
functions.forEach {
if (it is CallableReference) {
println(it.owner)
}
}

How to write custom chaining

I want to create custom chainig to prevent some repetition.
I am a bit unclear on how to define it
I have a lot of :
someLiveData.observe(this) { objectWithTextAndVisibility->
textView.text = objectWithTextAndVisibility.text
textView.visibility = objectWithTextAndVisibility.visibility
}
i want to write something that will look as follows
someLiveData.observe(this).bind(textView).on(text)
and it will do the same thing
is there a way to define this ?
If I understood your question correctly, a generic structure like this may achieve what you want to do:
infix fun <T> LiveData<T>.observe(owner: LifecycleOwner) = LiveDataHolder(this, owner)
class LiveDataHolder<T>(val liveData: LiveData<T>, val owner: LifecycleOwner)
infix fun <T, S> LiveDataHolder<T>.bind(subject: S) = LiveDataBinder(this, subject)
class LiveDataBinder<T, S>(val liveDataHolder: LiveDataHolder<T>, val subject: S)
infix fun <T : VisibilityCarrier> LiveDataBinder<T, TextView>.on(textSelector: (T) -> String) {
liveDataHolder.liveData.observe(liveDataHolder.owner) {
subject.text = textSelector(it)
subject.visibility = it.visibility
}
}
interface VisibilityCarrier {
val visibility: Int
}
This should give you good flexibility and prevent a lot of repetition, however at the cost of adding some obscurity to your code base, and not conveying what it does right alway.
Additional and different behaviors can be added by implementing new LiveDataBinder extension functions, like the on function, even for different kinds of objects and expected interfaces (instead of only this combination of TextView and VisibilityCarrier, even by maintaining the on name) and so on.
The use style can vary a lot with this, being like the ones below (considering Data implements VisibilityCarrier interface and provides a text String property):
liveData.observe(this).bind(textView).on { it.text }
liveData.observe(this) bind textView on { it.text }
liveData.observe(this).bind(textView).on(Data::text)
liveData.observe(this) bind textView on Data::text
This on implementation also allows for defining which property to use as text and different classes as well, like so:
anotherLiveData.observe(this).bind(textView).on(AnotherData::someText)
anotherLiveData.observe(this).bind(textView).on(AnotherData::anotherText)
UPDATE: After reading the explanation in the comments I think I got it, and believe this should address the point (where Data contains text and visibility properties):
infix fun <T : Data, S : View> LiveDataBinder<T, out S>.on(textProperty: KMutableProperty1<S, in String>) {
liveDataHolder.liveData.observe(liveDataHolder.owner) {
textProperty.set(subject, it.text)
subject.visibility = it.visibility
}
}
In the case of TextView and other Java defined classes, compiler will complain about synthetic access, and this problem could be addressed like in the following snippet (Kotlin views should be fine without this kind of workaround):
var TextView.text_: CharSequence
get() = text
set(value) { text = value }
And the usage would be like:
liveData.observe(this).bind(textView).on(TextView::text_)
liveData.observe(this).bind(customView).on(CustomView::someText)
UPDATE 2: A better approach as suggested by Tenfour04 (thanks). The following will avoid the synthetic property access compiler error:
infix fun <T : Data, S : View> LiveDataBinder<T, S>.on(textProperty: S.(String) -> Unit) {
liveDataHolder.liveData.observe(liveDataHolder.owner) {
subject.textProperty(it.text)
subject.visibility = it.visibility
}
}
And the usage would be like (without needing the text_ property extension):
liveData.observe(owner).bind(textView).on(TextView::setText)
After reading your comments on the other answer, I think I see what you're trying to do.
If I'm correct, you want to use the builder pattern to first bind something that is set (a setter), and then specify a getter/mapper of the data type to get some sub-data type that is applied with that setter.
So you can set up a couple of intermediate classes to do it like this:
fun <T> LiveData<T>.observe(owner: LifecycleOwner) = BindableObserver<T>().also { observe(owner, it) }
class BindableObserver<D>: Observer<D> {
private var boundSetter: BoundSetter<D, *>? = null
fun <S> bind(setter: (S)->Unit) = BoundSetter<D, S>(setter).also { boundSetter = it }
override fun onChanged(t: D) {
boundSetter?.execute(t)
}
}
class BoundSetter<D, S>(private val setter: (S)->Unit) {
private var dataGetter: ((D)->S)? = null
fun on(getter: (D)->S) {
dataGetter = getter
}
fun execute(newValue: D) {
val subData = dataGetter?.invoke(newValue) ?: return
setter.invoke(subData)
}
}
You can't simply pass a TextView to bind, because Kotlin won't know which property of TextView to set, so you pass the property using property syntax (::). Unfortunately, TextView has a bunch of setText() overloads, so you have to specify the input type as well.
Usage syntax would be like this:
someLiveData.observe(this)
.bind<String>(textView::setText)
.on(ObjectWithTextAndVisibility::text)
To avoid the need for specifying which function of a TextView to bind, you could add a helper function:
fun <D> BindableObserver<D>.bind(textView: TextView) = bind<String>(textView::setText)
and then usage would be closer to what you suggested:
someLiveData.observe(this)
.bind(textView)
.on(ObjectWithTextAndVisibility::text)
You could also use lambda syntax:
someLiveData.observe(this)
.bind(textView)
.on { it.text }

Kotlin Extension Function on Observable<T>.subscribe does not work

I'm trying to write an extension function for Observable.subscribe which automatically logs errors.
fun <T> Observable<T>.subscribeAndLogE(onNext: Consumer<in T>): Disposable =
subscribe(onNext, ErrorConsumer())
The ErrorConsumer works and presumably logs the error, but subscribeAndLogE does not accept lambdas like .subscribe() does.
observer.subscribe {
//works
}
observer.subscribeAndLogE {
//does not work
}
It says:
With that OnboardingScreen being whichever value T would normally be.
I don't see the original Consumer<in T> in Observable doing anything special to accept lambdas. What am I doing wrong here?
You are passing a parameter of type Consumer to the function. You need to pass a function for the lambda syntax to work. This would work the way you want to:
fun <T> Observable<T>.subscribeAndLogE(onNext: (it : T) -> Unit): Disposable =
subscribe({ onNext(it) },{ throwable -> Log(throwable) })
and use it like so:
observer.subscribeAndLogE {
//works
}

Getting access to an instance in a wrapper function

In Kotlin I have this function to wrap a transaction:
fun wrapInTransaction(code: () -> Unit) {
realmInstance.beginTransaction();
code.invoke()
realmInstance.commitTransaction();
}
How can I get access to realmInstance in the invoked code?
The easy solution here is to make code a function with receiver:
fun wrapInTransaction(code: Realm.() -> Unit) {
realmInstance.beginTransaction();
realmInstance.code()
realmInstance.commitTransaction();
}
Inside a lambda which you pass as code you will be able to use this to reference the RealmInstance and to use its members directly as if inside a member function.
Calling realmInstance.code() is just calling code with passing realmInstance as a receiver to it.
The other answers correctly demonstrate how to pass the RealmInstance object to the lambda. In addition, you can make the whole function an extension function which makes the call site a bit prettier:
fun Realm.wrapInTransaction(code: Realm.() -> Unit) {
//this is implicit
beginTransaction();
code()
commitTransaction();
}
The call site will look like this:
Realm.getInstance(this).wrapInTransaction {
createObject(User.class)
}
Change the wrapInTransaction function to accept an extensions method on realmInstance like so:
fun wrapInTransaction(code:Realm.() -> Unit){
realmInstance.beginTransaction();
realmInstance.code()
realmInstance.commitTransaction();
}
Then you can use it like:
wrapInTransaction {
println("realm instance is $this, instanceId: $instanceId")
}
Where for the sake of the example the Realm looks like:
class Realm {
val instanceId = 42
fun beginTransaction() {
}
fun commitTransaction() {
}
}
The above technique is possible thanks to Kotlin's Function Literals with Receiver that make it possible to set the this instance (receiver) within lambda function body. It makes it easy to build type safe builders that reassemble ones from Groovy or Ruby.
This answer provides more samples on the technique.