Kotlin Platform Types and Generics - kotlin

I am stuck at the last Kotlin Koans task 28 where I get these error messages when I try to call my partitionTo function:
Error:(25, 12) Kotlin: Type inference failed. Expected type mismatch: found: kotlin.Pair<kotlin.Collection<kotlin.String!>, kotlin.Collection<kotlin.String!>> required: kotlin.Pair<kotlin.List<kotlin.String>, kotlin.List<kotlin.String>>
Error:(30, 12) Kotlin: Type inference failed. Expected type mismatch: found: kotlin.Pair<kotlin.Collection<kotlin.Char!>, kotlin.Collection<kotlin.Char!>> required: kotlin.Pair<kotlin.Set<kotlin.Char>, kotlin.Set<kotlin.Char>>
I read that the exclamation mark at the end of a type marks a platform type. But then I would have expected the type java.lang.String! and not kotlin.String!. Do I have to enforce null checks somewhere? Maybe someone can help me with this last task. I am using IntelliJs Kotlin Plugin version 0.10.195.
This is my code:
fun List<String>.partitionWordsAndLines(): Pair<List<String>, List<String>> {
return partitionTo(ArrayList<String>(), ArrayList<String>()) { s -> !s.contains(" ") }
}
fun Set<Char>.partitionLettersAndOtherSymbols(): Pair<Set<Char>, Set<Char>> {
return partitionTo(HashSet<Char>(), HashSet<Char>()) { c -> c in 'a'..'z' || c in 'A'..'Z'}
}
inline fun <reified T> Collection<T>.partitionTo(first: MutableCollection<T>, second: MutableCollection<T>, predicate: (T) -> Boolean): Pair<Collection<T>, Collection<T>> {
for (element in this) {
if (predicate(element)) {
first.add(element)
} else {
second.add(element)
}
}
return Pair(first, second)
}

The problem is that you promised to return a pair of Lists:
fun List<String>.partitionWordsAndLines(): Pair<List<String>, List<String>> {
But in fact returned a pair of Collections:
inline fun <reified T> Collection<T>.partitionTo(...): Pair<Collection<T>, Collection<T>> {
A useful hint from the task:
The signature of the function 'toCollection()' from standard library may help you.
See it here: https://github.com/JetBrains/kotlin/blob/master/libraries/stdlib/src/generated/_Snapshots.kt#L207
P.S. Why did you want to use inline and reified on partitionTo?

Check you return types partitionWordsAndLines(): **Pair, List>**, extensions require List or Set where partitionTo returns Collection
Here is fixed version
inline fun <reified T, C: MutableCollection<T>> Collection<T>.partitionTo(first: C, second: C, predicate: (T) -> Boolean): Pair<C, C>

Related

Kotlin - output type from type parameter

I am parsing multiple CSV files and would like to provide my application with some generic parsers with logging capabilities. Is it possible to give some generic solution for it?
My try to do that is:
interface Converter<out T> {
fun convert(fieldName: String, value: String): T?
}
object DoubleMapper : Converter<Double> {
private val log = LoggerFactory.getLogger(this::class.java)
override fun convert(fieldName: String, value: String): Double {
log.info("Converting $fieldName: $value to Double")
return 123.3
}
}
object ConverterProvider {
private val log = LoggerFactory.getLogger(ConverterProvider::class.java)
inline fun <reified T : Any> getConverter(): (String, String) -> T {
return when (T::class) {
Double::class -> DoubleMapper::convert
Int::class -> IntMapper::convert
else -> {
throw java.lang.RuntimeException("We do not have mapper")
}
}
}
}
However, this does not compile, does kotlin provide such capabilities to have function return type depend on type parameter?
Your solution is almost correct one. The only problem is that the compiler is not smart enough to understand that you verified the type of T and you return the right type of the converter. You just need to cast the converter to T:
return when (T::class) {
...
} as (String, String) -> T
This cast is unchecked, meaning that the compiler can't guarantee at runtime that the cast is safe. However, as long as you return correct converter for the T, such cast should be safe and you can just suppress the warning:
#Suppress("UNCHECKED_CAST")
return when (T::class) {
...
} as (String, String) -> T

Kotlin DSL - type inference on returned value

I'm trying to introduce the following (simplified) DSL:
fun <T> myDsl(specFn: DslSpec<T>.() -> Unit) {
val value = DslSpec<T>().apply(specFn).fn!!()
println("value is: $value")
}
class DslSpec<T> {
internal var fn: (() -> T)? = null
fun getValue(fn: () -> T) {
this.fn = fn
}
}
fun testCase() {
myDsl {
getValue {
"abc"
}
}
}
But it fails to infer T based just on the returned type of getValue ("Not enough information to infer type variable T"). I kind of see how it could be a very hard task to do for a compiler, but thought maybe there are already some tricks to make constructs like this work?
If you're using a version of Kotlin < 1.6.0, you should add #BuilderInference to the specFn argument:
fun <T> myDsl(#BuilderInference specFn: DslSpec<T>.() -> Unit) {
...
}
https://pl.kotl.in/__xy04j88
If you're using a version >= 1.6.0, you should either use the annotation as well, or both your declarations and their usages must be compiled with the compiler argument -Xenable-builder-inference.

Kotlin - Infer type for one of two generic parameters

I am trying to create a function that has two generic types: one reified, and another derived from the context of its usage (since it is an extension function):
inline fun <reified E, A> Either<Throwable, A>.bypassLeft(transformation: Throwable.() -> A): Either<Throwable, A> =
when (this) {
is Either.Left -> when (value) {
is E -> value.transformation().right()
else -> this
}
else -> this
}
The idea would be to call the function just mentioning the reified type, something like:
a.bypassLeft<NoResultException> { "" }
In which "a" is an object of type Either<Throwable,String>
But the compiler is not letting me go away with it, and requires me to specify both generic types, instead of deriving the second one form the object calling the function.
It seemed quite a reasonable thing to be possible, but maybe I am wrong...
Is this possible to achieve? If so, what am I doing wrong?
It's not currently possible with a function to ascribe a single type argument and leave the other inferred. You can achieve what you want if you type the lambda arguments by changing your implementation to not use a receiver type.
I threw in there an additional impl that shows how type args can also be partially applied with a class or other surrounding scope.
import arrow.core.Either
import arrow.core.right
inline fun <reified E : Throwable, A> Either<Throwable, A>.bypassLeft(
transformation: (E) -> A //changed to regular arg not receiver
): Either<Throwable, A> =
when (this) {
is Either.Left -> when (val v = value) { //name locally for smart cast
is E -> transformation(v).right()
else -> this
}
else -> this
}
class Catch<A>(val f: () -> A) { //alternative impl with partial type app
inline fun <reified E : Throwable> recover(
recover: (E) -> A
): Either<Throwable, A> =
Either.catch(f).fold(
{
if (it is E) Either.Right(recover(it))
else Either.Left(it)
},
{
Either.Right(it)
}
)
}
suspend fun main() {
val x: Either<Throwable, Int> = Either.Left(StackOverflowError())
val recovered = x.bypassLeft {
s: StackOverflowError -> //here infers E
0 // here infers A
}
println(recovered) // Either.Right(0)
val notRecovered: Either<Throwable, Int> =
Catch {
throw NumberFormatException()
1
}.recover<StackOverflowError> { 0 }
println(notRecovered) // Either.Left(java.lang.NumberFormatException)
}
This is possible as of Kotlin v1.7.0 with the underscore operator.
The underscore operator _ can be used for type arguments. Use it to automatically infer a type of the argument when other types are explicitly specified:
interface Foo<T>
fun <T, F : Foo<T>> bar() {}
fun baz() {
bar<_, Foo<String>>() // T = String is inferred
}
In your example, it would be possible like this:
a.bypassLeft<NoResultException, _> { "" }

Kotlin local function with generic

I'm just fooling around with some Functional Programming techniques and tried to implement a generic drop function for a list. However it seems the types are getting shadowed. I'm wondering why I can't re-use a generic type declared.
The IDE doesn't want me to reuse the generic type
sealed class List<out A> {
fun <A> drop(n: Int): List<A> {
fun go(n: Int, l: List<A>): List<A> = when (n) {
0 -> l
else -> go(n - 1, l.tail())
}
return go(n, this)
}
}
The IDE says the following
Type mismatch.
Required:
List<A#1 (type parameter of chapter3.List.drop)>
Found:
List<A#2 (type parameter of chapter3.List)>
Is this not possible with inner local functions?
You don't need to redefine you A type again in the function, it cames from the sealed class
sealed class List<out A> {
fun drop(n: Int): List<A> {
fun go(n: Int, l: List<A>): List<A> = when (n) {
0 -> l
else -> go(n - 1, l.tail())
}
return go(n, this)
}
}

How to flatMap vavr Either with left variance annotated

My code
open class Fail(override val message: String, override val cause: Throwable?) : RuntimeException(message, cause)
data class ValidationFail(override val message: String, override val cause: Throwable?) : Fail(message, cause)
more fails will be defined there in the future
i have 2 functions
fun fun1(): Either<out Fail, A>
fun fun2(a: A): Either<out Fail, B>
when i try to invoke them like this fun1().flatMap{fun2(it)}
i got
Type mismatch: inferred type is (A!) -> Either<out Fail, B> but ((A!) -> Nothing)! was expected. Projected type Either<out Fail, A> restricts use of public final fun <U : Any!> flatMap(p0: ((R!) -> Either<L!, out U!>!)!): Either<L!, U!>! defined in io.vavr.control.Either
Code from vavr Either:
default <U> Either<L, U> flatMap(Function<? super R, ? extends Either<L, ? extends U>> mapper) {
Objects.requireNonNull(mapper, "mapper is null");
if (isRight()) {
return (Either<L, U>) mapper.apply(get());
} else {
return (Either<L, U>) this;
}
}
I guess o have this error because there is L in flatMap definition not ? extends L
Any workaround for this ?
In your particular case, you can make it compile by removing out variance from fun1 and fun2 return type. You shouldn't use wildcard types as return types anyway.
But it won't help if you have fun1 and fun2 defined this way:
fun fun1(): Either<ConcreteFail1, A>
fun fun2(a: A): Either<ConcreteFail2, B>
Replacing L with ? extends L in flatMap signature will not help either because of ConcreteFail2 not being a subtype of ConcreteFail1. The problem is that Either is supposed to be covariant, but there is no such thing as declaration-site variance in Java. Although there is a workaround using Either#narrow method:
Either.narrow<Fail, A>(fun1()).flatMap { Either.narrow(fun2(it)) }
Of course, it looks odd and must be extracted to a separate extension function:
inline fun <L, R, R2> Either<out L, out R>.narrowedFlatMap(
crossinline mapper: (R) -> Either<out L, out R2>
): Either<L, R2> = narrow.flatMap { mapper(it).narrow }
Where narrow is:
val <L, R> Either<out L, out R>.narrow: Either<L, R> get() = Either.narrow(this)
I think Vavr doesn't provide its own narrowedFlatMap because this method requires using a wildcard receiver type, so it can't be a member method and must be a static one, which breaks all readability of operations pipelining:
narrowedFlatMap(narrowedFlatMap(narrowedFlatMap(fun1()) { fun2(it) }) { fun3(it) }) { fun4(it) }
But since we use Kotlin, we can pipeline static (extension) functions as well:
fun1().narrowedFlatMap { fun2(it) }.narrowedFlatMap { fun3(it) }.narrowedFlatMap { fun4(it) }