Venkataramanan Parameswaran
02/20/2025, 6:22 PMsealed class UiState {
data class Success(val data: String): UiState()
data class Error(val message: String): UiState()
object Loading: UiState()
}
@OptIn(ExperimentalContracts::class)
fun UiState.isSuccess(): Boolean {
contract {
returns(true) implies(this@isSuccess is UiState.Success)
}
return this is UiState.Success
}
class SampleViewModel: ViewModel() {
var state: UiState by mutableStateOf(UiState.Loading)
init {
state = UiState.Success("Initial data")
}
fun doSomething() {
if (state.isSuccess()) {
state = state.copy(data = "New data")
}
}
}
In this in the doSomething
function I'm reassigning state with a new state value. Here in the previous line i have called an extension function isSuccess which has a contract that implies the state is Success. But in the next line it is not able to smart cast. Am i missing something?... My expectation here is it to be smart casted to Success state.
Ref: https://kt.academy/article/ak-contracts#implications-of-the-fact-that-a-function-has-returned-a-valueMichael Krussel
02/20/2025, 9:11 PMvar
property. And then even if it was a val, it would still fail because you are getting it from a property delegate.
Basically the compiler cannot guarantee that the getter for MutableState
will not change what it returns between the call to isSuccess
and the call to copy
.Venkataramanan Parameswaran
02/21/2025, 6:37 AMvar
property.
Let's take the below example,
fun printEachLine() {
var list: MutableList<String>? = mutableListOf()
if (!list.isNullOrEmpty()) {
for (e in list) {
println(e)
}
}
}
@OptIn(ExperimentalContracts::class)
inline fun <T> Collection<T>?.isNullOrEmpty(): Boolean {
contract {
returns(false) implies (this@isNullOrEmpty != null)
}
return this == null || this.isEmpty()
}
Here the list is var.... But since, the isNullOrEmpty has the contract that implies that the given collection is not null if it returns true... So, there is no compilation error. if we remove the contract in the isNullOrEmpty method, then it will throw the error.
2. Property delegate.
Here is another sample(in the image attached) in which i used State with val
and didn't use property delegate But still it is not smart casting.Michael Krussel
02/21/2025, 1:30 PMvalue
property in the MutableStateList
is a var
. Even if it was a val
you still could not smart cast, because the Kotlin compiler does not assume that val
properties from other jar
or klib
are read only, because they could be changed in the future to not be readonly, without recompiling your code.Michael Krussel
02/21/2025, 3:17 PMVenkataramanan Parameswaran
02/21/2025, 3:34 PMLeo N
02/22/2025, 2:48 PMclass ExampleViewModel: ViewModel (){
val _uiState: MutableStateFlow<UiState> = MutableStateFlow(UiState.Idle)
val uiState = _uiState.asStateFlow()
}
@Composable
fun ExampleComposable(viewModel: ExampleViewModel){
val uiState by viewModel.uiState.collectAsState()
}
I have to use val in size when again
when (val state = uiState){
UiState.Loading -> {
///
}
}