https://kotlinlang.org logo
#getting-started
Title
# getting-started
m

Mark

06/10/2022, 5:10 AM
Why does this extension function not call
SpannableStringBuilder.inSpans()
Copy code
fun <A : Appendable> A.inSpans(vararg spans: Any, builderAction: A.() -> Unit): A =
    if (this is SpannableStringBuilder) {
       // PROBLEM: doesn't call SpannableStringBuilder.inSpans()
       inSpans(spans = spans, builderAction = builderAction) 
    } else {
        builderAction()
        this
    }
I was able to fix by changing to:
Copy code
fun <A : Appendable> A.inSpans(vararg spans: Any, builderAction: A.() -> Unit): A {
    if (this is SpannableStringBuilder) {
        inSpans(spans) {
            builderAction()
        }
    } else {
        builderAction()
    }
    return this
}
It seems the key part is to specify the lambda explicitly rather than passing the
builderAction
reference
e

ephemient

06/10/2022, 5:17 AM
because
builderAction: A.() -> Unit
so overload resolution can only choose your local function. when wrapped in the lambda, the receiver it's being called on is the outer
A
, not the inner
SpannableStringBuilder
👍 1
also in your second variant, if you're not writing
spans = spans
or
*spans
, you're invoking the
span =
overload
🙏 1
m

Mark

06/10/2022, 5:30 AM
That
spans = spans
issue is a nasty one (IMO an API design flaw). I actually had it like that originally, but mistakenly forgot about the easy-to-make mistake.
Are you saying the lambda should be
(this as A).builderAction()
?
e

ephemient

06/10/2022, 5:41 AM
no, I'm saying that when you write
{ builderAction() }
it actually means
{ this@inSpans.builderAction() }
, not
{ this.builderAction() }
- that's what makes it different than passing it directly
m

Mark

06/10/2022, 5:48 AM
Have you got those the wrong way around? In this case don’t we want
{ this.builderAction() }
?
e

ephemient

06/10/2022, 5:49 AM
you want that but you can't have it because
this: SpannableStringBuilder
does not match
builderAction: A.() -> Unit
so your lambda is silently not behaving as you seem to expect
if you changed it to
builderAction: Appendable.() -> Unit
it would be applicable
m

Mark

06/10/2022, 5:55 AM
Okay, so that would be a good solution, but
(this as A).
would also work right?
e

ephemient

06/10/2022, 5:56 AM
it's potentially unsafe:
A
could be a subtype of
SpannableStringBuilder
(there aren't any by default, but it's not a final class)
in other words,
this is SpannableStringBuilder
does not imply
<SpannableStringBuilder : A>
👍 1
2 Views