Inferring only some type parameters in Kotlin - kotlin

I have a method with two type parameters, only one of which can be inferred from arguments, something like (no need to comment this cast is evil, the body is purely for the sake of example)
fun <A, B> foo(x: Any, y: A.() -> B) = (x as A).y()
// at call site
foo<String, Int>("1", { toInt() })
However, the compiler can tell B is Int if A is String. And more generally, if it knows A, B can be inferred.
Is there a way to only provide A at the call site and infer B?
Of course, the standard Scala approach works:
class <A> Foo() {
fun <B> apply(x: Any, y: A.() -> B) = ...
}
// at call site
Foo<String>().apply("1", { toInt() })
I was interested in whether Kotlin has a more direct solution.

Based on this issue/proposal, I'd say no(t yet):
Hello, I am proposing two new feature for kotlin which go hand in
hand: partial type parameter list and default type parameters :) Which
in essence allows to do something as the following:
data class Test<out T>(val value: T)
inline fun <T: Any, reified TSub: T> Test<T>.narrow(): Test<TSub>{
return if(value is TSub) Test(value as TSub) else throw ClassCastException("...")
}
fun foo() {
val i: Any = 1
Test(i).narrow<_, Int>() // the _ means let Kotlin infer the first type parameter
// Today I need to repeat the obvious:
Test(i).narrow<Any, Int>()
}
It would be even nicer, if we can define something like:
inline fun <default T: Any, reified TSub: T> Test<T>.narrow(): Test<TSub>{
return if(value is TSub) Test(value as TSub) else throw ClassCastException("...")
}
And then don't even have to write _
fun foo() {
val i: Any = 1
Test(i).narrow<Int>() //default type parameter, let Kotlin infer the first type parameter
}

Related

Make class extend multiple function type interfaces with the same signature in Kotlin

I want to make a class extend multiple function type interfaces.
This works since the function types have different signatures, () -> Unit and (String) - Unit
typealias A = () -> Unit
typealias B = (something: String) -> Unit
class Test : A, B {
override fun invoke() {
TODO("Not yet implemented")
}
override fun invoke(something: String) {
TODO("Not yet implemented")
}
}
Now if I add a third function type, the compiler complains about Conflicting Overloads or A supertype appears twice
typealias A = () -> Unit
typealias B = (something: String) -> Unit
typealias C = (somethingElse: String) -> Unit
class Test : A, B, C {
override fun invoke() {
TODO("Not yet implemented")
}
override fun invoke(something: String) {
TODO("Not yet implemented")
}
override fun invoke(somethingElse: String) {
TODO("Not yet implemented")
}
}
I can obviously go and add garbage params to C to make it work, but this seems more like a hack
typealias C = (somethingElse: String, garbage: Unit?) -> Unit
but now if I define type D with the same signature,
typealias D = (somethingElseElse: String, garbage: Unit?) -> Unit
I would run into the same issue.
I thought that maybe value classes could help here:
#JvmInline
value class BString(val value: String)
#JvmInline
value class CString(val value: String)
typealias A = () -> Unit
typealias B = (something: BString) -> Unit
typealias C = (somethingElse: CString) -> Unit
class Test : A, B, C {
override fun invoke() {
TODO("Not yet implemented")
}
override fun invoke(something: BString) {
TODO("Not yet implemented")
}
override fun invoke(somethingElse: CString) {
TODO("Not yet implemented")
}
}
... but since value classes are compiled out of existence, that too is not a solution
Platform declaration clash: The following declarations have the same
JVM signature (invoke(Ljava/lang/Object;)Ljava/lang/Object;):
I'm assuming Kotlin KEEP 302, Binary Signature Name (https://github.com/Kotlin/KEEP/blob/binary-signature/proposals/multiplatform/binary-signature.md), would solve this issue in the future, but what is the correct way in the meantime to implement multiple function interfaces with the same signatures?
Practical use-case that I can think of: let's say you want to have a class that can handle Clickable and DoubleClickable, both would have something like (Event) -> Unit
EDIT: based on #mateusz's answer, this works, but only when using value classes, not if interface B and C are using normal Strings:
#JvmInline
value class BString(val value: String)
#JvmInline
value class CString(val value: String)
interface A {
operator fun invoke()
}
interface B {
operator fun invoke(something: BString)
}
interface C {
operator fun invoke(somethingElse: CString)
}
class Test : A, B, C {
override operator fun invoke() {
println("invoke A")
}
override operator fun invoke(something: BString) {
println("invoke B - something = $something")
}
override operator fun invoke(somethingElse: CString) {
println("invoke C - somethingElse = $somethingElse")
}
}
fun main(args: Array<String>) {
val handlerA = A::invoke
val handlerB = B::invoke
val handlerC = C::invoke
val t = Test()
handlerA(t)
handlerB(t, BString("hello B"))
handlerC(t, CString("hello C"))
}
outputs:
invoke A
invoke B - something = BString(value=hello B)
invoke C -somethingElse = CString(value=hello C)
The completer does not care about parameter's names.
The fun test(a: String): String and fun test(b: String): String are the same functions. When you will call test("some") then which function should be called?
You can create dedicated interfaces:
interface Clickable {
fun click(param: String)
}
interface DoubleClickable {
fun fastDoubleClick(param: String)
fun slowDoubleClick(param: String)
}
Then you can use function references if you want val handleClickFun: String -> Unit = Clickable::click
This will never work. At the fundamental JVM level, you can't implement the same interface twice with different generics. I would not expect this to ever work, even with the KEEP you mention.
Why do you want to extend function interfaces at all? If you just want the nice call syntax, you can have separate operator fun invoke overloads, without overriding anything. But even better would be using functions with actual names. If you need to pass it to methods accepting lambdas, use method references, e.g. Test::handleClick and Test::handleDoubleClick.
A typealias is just a way to give a convenient label to a specific type - it's not a type in itself, anywhere you specify that typealias, you can can just pass in a variable defined as the real type, or any other typealias you've derived from it.
So B and C are the same thing. You can have two different aliases for the same thing if that makes sense in different parts of your code (that's kinda the whole point of them! Relabel types to make them more readable or understandable) but that's just ways to refer to a type.
But when it comes to defining your class, it makes no sense. B and C are the same type, you're repeating yourself (and the compiler will give you a supertype appears twice error). And to implement that one type, you need one function - and only one, because if you have two identical functions then which one would get called?
So you can do this if you want:
typealias A = () -> Unit
typealias B = (something: String) -> Unit
typealias C = (somethingElse: String) -> Unit
class Test : A, B {
override fun invoke() {
println("invoke")
}
override fun invoke(something: String) {
println("invoke: $something")
}
}
fun doAThing(thing: C) {
thing("wow")
}
fun main() {
doAThing(Test())
}
doAThing takes a C, so we can pass it a B, because B is C.
I'm guessing that's not very useful to you, but that's the limitation of typealiases, and bare function types in general. If you want two separate functions with the exact same signature in the same scope, you need to be able to refer to them explicitly - and that usually means giving them different names.
How is your click-handler class going to handle your Event if you can't tell it whether it's a single or double-click? And even if you could (e.g. through something like (handlerFunction as B).invoke(event)) then which of your identical overridden functions in the class is which?
Like Mateusz says, you need to use interfaces, and then you can pass references to the functions, because you have a name for each one you can refer to. The things you're passing those functions into can define the types using typealiases if they want. And if you want a type that can handle both kinds of clicks, create another interface that implements both types.
If you want to be able to pass a single object that has multiple functions with the same signature, that's what you need. If you want to use function types instead, you'll have to pass the individual function references in - but something somewhere has to be able to distinguish between them in the first place, and that's usually where they're defined

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, _> { "" }

Map return type from input generic type in Kotlin

I have a function that returns IMyInterface
fun getValue(type: Types): IMyInterface? {}
But I have to always cast the return type in this way before I can use it:
getValue(Types.TypeInt)?.let { value ->
val usableVale = MyInterfaceAsInt.cast(value)
// more code...
}
MyInterfaceAsInt implements IMyInterface and I have no control over them.
The casting always depend of the input, so
Types.TypeInt -> MyInterfaceAsInt.cast(value)
Types.TypeLong -> MyInterfaceAsLong.cast(value)
...etc
Is there a way to define somthing like fun <T = Types> getValue(type: T) in a way that the return type can be inferred from type ?
I would like to do the casting inside getValue.
It looks like Types.TypesInt/Long/etc. are simply instances of the same type Types, not different types; and in fun <T> getValue(type: T), T has to be a type. So it doesn't seem to be possible.
But I would probably go the other way and define functions like
fun getValueAsInt(): MyInterfaceAsInt? = getValue(Types.TypeInt)?.let { MyInterfaceAsInt.cast(it) }
fun getValueAsLong(): MyInterfaceAsLong? = getValue(Types.TypeLong)?.let { MyInterfaceAsLong.cast(it) }
...
Another alternative which could be useful at least when the type can be inferred:
#Suppress("UNCHECKED_CAST")
inline fun <reified T : MyInterface> getValue(): T? = when(T::class) {
MyInterfaceAsInt::class -> getValue(Types.TypeInt)?.let { MyInterfaceAsInt.cast(it) }
MyInterfaceAsLong::class -> getValue(Types.TypeLong)?.let { MyInterfaceAsLong.cast(it) }
...
} as T

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) }

Is it possible to make safe inline Optional in Kotlin?

In Kotlin sometimes I have to work with double nullability. For example, I need double nullability, when I want to use T? where T may be a nullable type. There are a few approaches for doing this:
Holder<T>? where Holder is data class Holder<out T>(val element: T) - example1
boolean flag variable - example1
containsKey for Map<K, T?> - example1
The special UNINITIALIZED_VALUE for representing the second kind of null - example1
The last approach has the best performance, but it's also the most error-prone. So I've decided to encapsulate it in inline class Optional<T>:
inline class Optional<out T> #Deprecated(
message = "Not type-safe, use factory method",
replaceWith = ReplaceWith("Optional.of(_value)")
) constructor(private val _value: Any?) {
val value: T?
get() =
#Suppress("UNCHECKED_CAST")
if (isPresent) _value as T
else null
val isPresent: Boolean
get() = _value != NULL
companion object {
#Suppress("DEPRECATION")
fun <T> of(value: T) = Optional<T>(value)
fun <T : Any> ofNullable(value: T?): Optional<T> =
if (value == null) EMPTY
else of(value)
#Suppress("DEPRECATION")
val EMPTY = Optional<Nothing>(NULL)
}
private object NULL
}
inline fun <T> Optional<T>.ifPresent(code: (T) -> Unit) {
#Suppress("UNCHECKED_CAST")
if (isPresent) return code(value as T)
}
inline fun <T> Optional<T>.or(code: () -> T): T {
ifPresent { return it }
return code()
}
The first problem with this Optional is public constructor, which allows creating instances with arguments of not matching type.
The second problem was noticed at testing time. Here is the failed test:
emptyOr { Optional.EMPTY }.value assertEql null
fun <T> emptyOr(other: () -> T): T = Optional.EMPTY.or(other)
Exception:
Exception ClassCastException: Optional$NULL cannot be cast to Optional
at (Optional.kt:42) // emptyOr { Optional.EMPTY }.value assertEql null
If I remove inline modifier from Optional, the test will pass.
Q: Is there any way to fix these problems without removing inline modifier from Optional?
1 Examples include some context. Please read them fully before writing that I added incorrect links.
I implemented exactly the same utility in one of my projects: OptionalValue.kt. My implementation is very similar to yours, it is also an inline/value class, so it should be cpu/memory efficient and it passes all tests I throw at it.
Regarding your first question: about a public constructor. There is an annotation specifically for this case: #PublishedApi. I tried to reproduce ClassCastException from your example, but it worked for me without problems, so I believe it was a bug in Kotlin itself (?).
Also, to answer the question why do we need double nullability, I explained my point here