Say I want to declare a simple algebraic datatype for integer lists:
sealed class IntList
data class Cons(val head: Int, val tail: IntList): IntList()
data class Nil() : IntList()
However, the last declaration results in an error
Data class must have at least one primary constructor parameter
- Why is this limitation present? Looking at the documentation, there seems to be no good technical reasons for requiring data class constructors to be non-nullary.
Is it possible to express nullary constructors without having to write lots of boilerplate code? If I change the last declaration to something like
sealed class Nil() : IntList()then I lose the free implementations of
hashCode()andequals()that come for free withdata classdeclarations.
EDIT
Alex Filatov gave a nice short solution below. Obviously, you never need more than one instance of Nil, so we can just define a singleton object
object Nil : IntList()
However, what would we do if our lists were parameterized by a type parameter? That is, now the first two lines of our definition would be
sealed class List<A>
data class Cons<A>(val head: A, val tail: List<A>): List<A>()
We cannot declare a polymorphic singleton Nil object which derives from List<A> for any A, since we have to provide a concrete type for A at the time of declaration. The solution (taken from this post) is to declare A as a covariant type parameter and declare Nil as a subtype of List<Nothing> as follows:
sealed class List<out A>
data class Cons<A>(val head: A, val tail: List<A>): List<A>()
object Nil : List<Nothing>()
This allows us to write
val xs: List<Int> = Cons(1, Cons(2, Nil))
val ys: List<Char> = Cons('a', Cons('b', Nil))