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)
}
}
Related
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, _> { "" }
I'm trying to write serialization functions to be able to serialize any vector (=ArrayList) in Kotlin, as well as primitive types and classes extending a Serialize class having a toBinary() function.
I also have a custom WriteDataStream class (code below) to serialize fields with the right format, endianness, etc.
I'm new to Kotlin but have experience in C++. In C++, I used templates and template specialization to solve that problem easily, but with Kotlin I've been struggling for a few days, without success.
I have a custom vector class MyVector which extends ArrayList and adds a maximum size. I want to serialize it with any generic type T, including inner vectors like a MyVector<MyVector<MyClass>>.
My WriteDataStream contains the following:
inline fun <reified T> write(vector: MyVector<T>) {
this.writeSize(vector.size.toULong(), vector.MAX_SIZE)
for (element in vector) {
write<T>(element)
}
}
inline fun <reified T: Serialize> write(value: T) {
writeSerialize(value as Serialize)
}
inline fun <reified T> write(value: T) {
when (T::class) {
UByte::class -> {
writeUInt8(value as UByte)
}
UShort::class -> {
writeUInt16(value as UShort)
}
UInt::class -> {
writeUInt32(value as UInt)
}
ULong::class -> {
writeUInt64(value as ULong)
}
Byte::class -> {
writeInt8(value as Byte)
}
Short::class -> {
writeInt16(value as Short)
}
Int::class -> {
writeInt32(value as Int)
}
Long::class -> {
writeInt64(value as Long)
}
Boolean::class -> {
writeBoolean(value as Boolean)
}
Float::class -> {
writeFloat(value as Float)
}
Double::class -> {
writeDouble(value as Double)
}
else -> {
error("Default serialization:" + T::class.qualifiedName)
}
}
}
All the underlying functions writeXXX() are tested and work fine. However, when tying to serialize a MyVector with a class extending Serialize, I fall in the "Default serialization" case:
#Test
fun writeVectorOfStructure() {
class TestStructure: Serialize() {
override fun toBinary(stream: WriteDataStream) {
stream.writeUInt32(17U)
stream.writeUInt8(3U)
stream.writeDouble(555.555)
}
}
val value = MyVector<TestStructure>(MAX_SIZE, arrayListOf(TestStructure(), TestStructure()))
writeStream.write(value)
val bytes: UByteArray = writeStream.byteArray()
Assert.assertEquals(bytes.size, 28) // = 2 (for size) + 2*(4+1+8) = 28 bytes
}
So my question is: Why does Kotlin not use the function
inline fun <reified T: Serialize> write(value: T)
when it serializes an element of the vector (write<T>(element)) with generic T = Serialize, but instead uses the more generic one?
inline fun <reified T> write(value: T)
In C++, the compiler always uses the most fitted function.
Is there a way to overcome this limitation in Kotlin?
I have tried with and without reified types, I have tried a non-generic function as well: inline fun write(value: Serialize), but without success. The only thing that seems to work was to add a case for classes "instance of" Serialize in the fully-generic inline fun <reified T> write(value: T), but this is not really a nice solution.
Thanks you !
JVM and its bad implementation of generics
You are a victim of Java's implementation of generics, more specifically the erasure. C++ uses what is called type expansion to implement generics, meaning if you declare MyType<A> and MyType<B>, at runtime you will have two different types, language runtime will create them for you.
On the other hand what Java does is called the erasure implementation. so in java world when you say List<String> and List<Integer>, at runtime they are both identical types, that is system doesn't have any information to make a distinction between both of these, they are List type (Note that there is no type parameter, it got removed during the compilation).
Lets decompile your code and see for yourself
I wrote following code in kotlin, it matches yours
class SomeType {
inline fun <reified T: String> write(value: T) {}
inline fun <reified T> write(value: T) {}
inline fun <reified T: Any> write(vector: List<T>) {
for (element in vector) {
write(element)
}
}
}
And when I decompile the code it gives me following. (Only relevant code included)
public final class SomeType {
public final void write(#NotNull String value) {}
public final void write(Object value) {}
public final void write(#NotNull List vector) {
boolean var6;
for(Iterator var4 = vector.iterator(); var4.hasNext(); var6 = false) {
Object element = var4.next();
}
}
}
Look at the write(vector: List<T>) method's decompilation. parameter type got changed to List which is a Raw Type and its components are objects.
And for an Object best method match is public final void write(Object value) and not the one with String or in your case Serialize.
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
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) }
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
}