Laxystem
01/27/2024, 2:02 PMprivate fun firePositionChangeEvent() {
val x = x
val y = y
if (x != null && y != null) {
tile = onPositionChange(x, y)
}
}
// becomes
private fun firePositionChangeEvent() {
tile = if (catch val x != null && catch val y != null) onPositionChange(x, y) else null
}
Created KT-65359, proposing adding catch val
, a way to "catch" the current value of a mutable/delegated property for smartcasting purposes.jw
01/27/2024, 3:32 PMLaxystem
01/27/2024, 3:35 PMnullable
isn't null
? Nope, it's different...jw
01/27/2024, 3:36 PMlet
to capture the subject in a local, like we can already do with a val
in a when
Laxystem
01/27/2024, 3:37 PMval foo = etc
to be used everywhere?jw
01/27/2024, 3:37 PMLaxystem
01/27/2024, 3:38 PMJeff Lockhart
01/27/2024, 6:11 PMif let x = x, let y = y {
tile = onPositionChange(x, y)
} else {
tile = null
}
In Kotlin this might be:
tile = if (val x = x && val y = y) {
onPositionChange(x, y)
} else {
null
}
The variable assignment evaluates to true if not null. And in Swift the ,
is equivalent to &&
logical operator.
This would be equivalent to your example:
tile = if (catch val x != null && catch val y != null) {
onPositionChange(x, y)
} else {
null
}
(You have the null
and onPositionChange
swapped in your code.)Jeff Lockhart
01/27/2024, 6:12 PMcatch
is a good keyword for this usage, since it already has a purpose in catching exceptions. capture
would be more indicative of the behavior, but that's a longer syntax than the Swift construct proposal.Laxystem
01/27/2024, 6:38 PM(You have theOh, didn't notice, thanks!andnull
swapped in your code.)onPositionChange
I'm not sureFair. It was an example keyword. I would like to link Java's keyword creation guidelines (de juro, it's a draft, but de facto, it's already used). I think the existing keyword is a nice match, and I don't think it conflicts too much - in Kotlin, `catch`es always show with curly braces around them (is a good keyword for this usage, since it already has a purpose in catching exceptions.catch
} catch {
). But it's a question of what's more important for Kotlin, backwards compatibility, or non-confusing keywords.
Yeah, I really don't want to see that in Kotlin code. It's just boilerplate-y. The only place whereval x = x
val x = x
is better than catch val x
is top-level mutable properties, because they cannot be accessed via an explicit receiver -- that is, once you "catch" them, it's forever, you cannot access the original property anymore. However, my reply to this is: Don't use mutable top-level properties.
You could introduce a way to de-declare a local property tho --- drop x
?Jeff Lockhart
01/27/2024, 7:19 PMcatch val x
that doesn't do a != null
check? The Swift construct does the logical null check automatically, making it a more concise syntax.
I would expect both syntaxes' variables are only valid for the scope of the if block. This is consistent with Kotlin (and other languages) variable scoping.Jeff Lockhart
01/27/2024, 7:21 PMguard
language feature, which allows capturing a non-null variable for the scope of the function following the guard
. E.g.:
guard let x = x, let y = y else {
tile = null
return
}
tile = onPositionChange(x, y)
This would be similar to Kotlin:
val x = x ?: run {
tile = null
return
}
val y = y ?: run {
tile = null
return
}
tile = onPositionChange(x, y)
where you have to check variables individually, similar to the if capture non-null checks, which can be chained in Swift, but isn't possible now with Kotlin's let
, which I assume is a similar goal to your proposal.Ruckus
01/27/2024, 8:51 PMcatch val x
that doesn't do a != null
check?
>
Perhaps for type refinement
if (catch val x is Foo) ...
Laxystem
01/28/2024, 11:03 AMis
expressions, expensive property computation, etc.
Note this syntax isn't exclusive to ifs - but to any place where a property is used (even a simple catch val x
, without using the variable), so limiting it to the boundaries of the if might be confusing.
Swift's guard
can be implemented via a Kotlin stdlib function -- it is basically a fancy if:
if (catch val x == null || catch val y == null) {
tile = null
return
}
tile = onPositionChange(x, y)
This is a very Java way to do this, I don't think it fits Kotlin.Wout Werkman
01/30/2024, 5:51 AMtile = x?.let { x -> y?.let { y -> onPositionChange(x, y) } }
You might want to focus on use cases where this is not possible.Michael de Kaste
01/30/2024, 12:55 PMinline fun <R, A, B> withNoNulls(p1: A?, p2: B?, function: (p1: A, p2: B) -> R): R? =
p1?.let { p2?.let { function.invoke(p1, p2) } }
which becomes
withNoNulls(x,y){ x, y -> onPositionChange(x,y) }
or better yet
withNoNulls(x,y,::onPositionChange)
Laxystem
02/03/2024, 11:02 AMfield
-less, and delegated properties.
Beyond smartcasting, those properties are evaluated every time they're used. Developers unaware of this might not use val x = x
, or might even decide the code ugliness is worth the performance hit (which is much common than one would think it is, I've done this myself a lot more than I would like to admit). By having catch val
, one could implement warnings for using said properties a lot or in suspend
functions (potentially together with @NonExpansiveThreadSafeProperty
and @ExpansiveThreadUnsafeProperty
stdlib annotations). Even worse than a performance hit, this can cause thread safety issues (as we've mentioned before) - using catch val
for all properties as a good-code-standard (unless, of course, you need the actual, current value) could mitigate those from appearing in the first place.