When discussing calling generic functions Generics: in, out, where | Kotlin Documentation says “Type arguments can be omitted if they can be inferred from the context”.
Is that only the case if all type arguments can be inferred, or is it the case that if you need to provide one type argument you must provide all of them (even if some of them are provided as _
)?
My motivation for this is as follows:
I’m trying to make some code a bit more ergonomic.
I’m modelling data using GitHub - michaelbull/kotlin-result at 1.1.21. This is a Result<V, E>
monad, where V
is the “success” type, E
is the error type.
For most data I have a sealed interface to represent states “data is loading” and “data is loaded”, and classes for errors, e.g.
sealed interface Data {
data object Loading : Data
data class Loaded(val data: String) : Data /* String for demo purposes */
}
sealed interface AppError {
data class NetworkError(...)
data class JsonError(...)
// etc
}
fun someApiCall(...) : Result<Data, AppError>
Kotlin-Result provides a map
inline function that operates over the Result
type, applying a function to the value if it’s Ok
, or returning the error unchanged. I generally only need to operate on the contained data if the sub-type is Data.Loaded
, so this leads to code like this:
val newData = data.map {
when (it) {
is Data.Loading -> it,
is Data.Loaded -> { /* transform the data in some way */ }
}
}
I thought a more ergonomic approach would be to be able to write something like:
val newData = data.mapIfInstance<Data.Loaded> { /* transform the data in some way */ }
and wrote this to do so:
public inline infix fun <V, E, reified T : V> Result<V, E>.mapIfInstance(transform: (T) -> V): Result<V, E> {
return when (this) {
is Ok -> (value as? T)?.let { Ok(transform(it)) } ?: this
is Err -> this
}
}
However, if I call this with just the type parameter T, e.g:
val newData = data.mapIfInstance<Data.Loaded> { /* transform */ }
I get:
Inapplicable candidate(s): fun <V, E, reified T : V> Result<V, E>.mapIfInstance(transform: (T) -> V): Result<V, E>
If I call it with all three type parameters it works:
val newData = data.mapIfInstance<Data, AppError, Data.Loaded> { /* transform */ }
I’ve made a few different changes (e.g., to the order of the type paramters in the function declaration) to see if I can get the compiler to infer the missing types V and E when I only provide T, but with no success.
Playground code that shows the problem at Kotlin Playground: Edit, Run, Share Kotlin Code Online
Is what I want to achieve possible?