Reputation: 519
I'm trying to rewrite the List interface as an exercise for learning Functional Programming in Kotlin, but I'm not able to figure out why I'm getting an error when I'm trying to have an object as the empty list, that doesn't occur in the Kotlin standard library. In my code, see below, I want to use NIL as a singleton empty list, using list() as the function to return it. However, this generates a type mismatch error on the function of type "Required List. Found NIL"
interface List<A> {
val empty: Boolean
val head: A
val tail: List<A>
fun cons(a: A): List<A> = Cons(a, this)
}
object NIL : List<Nothing> {
override val empty: Boolean = true
override val head: Nothing
get() = throw IllegalStateException("head called on empty list")
override val tail: List<Nothing>
get() = throw IllegalStateException("tail called on empty list")
}
private class Cons<A>(override val head: A,
override val tail: List<A>) : List<A> {
override val empty: Boolean = false
}
fun <A> list(): List<A> = NIL // Type mismatch. Required: List<A>. Found: NIL
fun <A> list(vararg a: A): List<A> {
var n = list<A>()
for (e in a.reversed()) {
n = Cons(e, n)
}
return n
}
In the standard library, this error doesn't occur, as you can see in EmptyList in Collections.kt. Am I doing something wrong or am I missing some concepts that make the latter possible and the former not?
Of the three solutions proposed, the only one that allows passing the test code below is the one using the anonymous object, while the others throw the exception java.lang.ClassCastException: java.lang.String cannot be cast to java.lang.Void
.
assertEquals("a", list<String>().cons("a").head)
However, if I change NIL
to NIL : List<Any?>
, the solution NIL as List<A>
works without any problem.
Upvotes: 4
Views: 1227
Reputation: 30686
you can cast a List<Nothing>
to a List<A>
explicit by as
keyword, for example:
fun <A> list(): List<A> = NIL as List<A>;
IF you want to suppress the compiler warnings, you can annotated the function with @Suppress
annotation, for example:
@Suppress("UNCHECKED_CAST")
fun <A> list(): List<A> = NIL as List<A>;
OR if you don't like this way, you can using an anonymous object in a funtion instead, for example:
fun <A> list(): List<A> = object : List<A> {
override val empty: Boolean = true
override val head: A
get() = throw IllegalStateException("head called on empty list")
override val tail: List<A>
get() = throw IllegalStateException("tail called on empty list")
};
Upvotes: 0
Reputation: 1894
Generic on your interface should be <out A>
instead of <A>
.
Also, IntelliJ Idea shows a warning when you use generics without out variance in such cases.
Upvotes: 2