I always struggle with this problem, when I don’t ...
# random
d
I always struggle with this problem, when I don’t have Arrow’s Optics in the code-base 😄 How to avoid the
else
branch? https://pl.kotl.in/NB1wSZ8su
Copy code
sealed interface Base

data class One(
    val name: String,
    val count: Int
) : Base

data class Two(
    val name: String,
    val count: Int
) : Base

fun <T : Base> T.rename(newName: String): T =
    when (this) {
        is One -> copy(name = newName)
        is Two -> copy(name = newName)
        // else branch required!
    }
e
y
Also, even when you add the
else
branch, you need a cast as
T
because
copy
methods aren't seen as returning
self
automatically. Btw, this works:
Copy code
fun <T : Base> T.rename(newName: String): T = with<Base, _>(this){ 
    when (this) {
        is One -> copy(name = newName)
        is Two -> copy(name = newName)
    } as T
}
and so does:
Copy code
fun <T : Base> T.rename(newName: String): T {
    val base: Base = this
    return when (base) {
        is One -> base.copy(name = newName)
        is Two -> base.copy(name = newName)
    } as T
}
e
also the approach I would take is
Copy code
fun One.rename(newName: String): One = copy(name = newName)
fun Two.rename(newName: String): Two = copy(name = newName)
fun Base.rename(newName: String): Base = when (this) {
    is One -> rename(newName)
    is Two -> rename(newName)
}
all the callers with specific types get the right resulting types, and callers with wider types don't care about the specifics anyway
d
Oh, that’s great! Damn I didn’t know we could define partial generic parameters using
_
😅
@ephemient the problem is that
Copy code
val base: Base = One(…)
base.rename(newName)
would return
Base
, no? 🤔
e
yes but it would with your version too
T
will be inferred to
Base
d
Sorry, let me elaborate 😄 In my case I have
Copy code
class Something<T : Base>(
  val t: T
) {

  fun someFun() {
    t.rename() <- would this be `Base` or `T`?
  }
}
e
ok in that case you really want lens/optics 😜
d
😄 Ye, I really miss it 🙂 However @Youssef Shoaib [MOD]’s solutions seems to be working nicely, besides the boilerplate that will confuse us every single time we will read the code 😛
e
I think you can write it less strangely confusingly as
Copy code
fun Base.rename(...) = when (val subject: Base = this) {
    is One -> ...
d
That looks better too. I went for this
Copy code
fun <T: Base> T.copying(block: Base.() -> Base): T = with(this) {
    @Suppress("UNCHECKED_CAST")
    block() as T
}
https://pl.kotl.in/-qGOD2BnS We gotta trust the user to not do weird things -i.e. returning wrong
T
-, but seems a fair compromise 🙂
k
If I may highjack the thread now that a solution has been found... out of interest, why doesn't the following work? The compiler complains because
Base
doesn't have a definition of
copy
. Shouldn't
this
be smart-cast to the corresponding
One
or
Two
?
Copy code
when (this as Base) {
        is One -> copy(name = newName)
        is Two -> copy(name = newName)
    } as T
y
I think it's that the expression
this as Base
is the one getting smartcast against
One
or
Two
. I agree with you, though, the compiler should understand that
this as Base
and
this
are one and the same
k
isn't this something that will be resolved in 2.0?
d
I doesn’t seem like it sad panda hope to be wrong
🫤 1