I am working my way through Scala (ver. 2.13.2
), and here I've defined a simple linked list with a trait ListSeq
. Also, I wanted to override a toString
method for pretty-printing. For this, I decided to use pattern-matching. The desired result can be seen in assert
cases
sealed trait ListSeq {
override def toString: String = s"[$elemSequence]"
private def elemSequence: String = {
this match {
case ListPair(hd, tl @ ListPair(_, _)) => s"$hd, ${tl.elemSequence}"
case ListPair(hd, EmptyList) => s"$hd"
case EmptyList => ""
}
}
}
case class ListPair(head: Int, tail: ListSeq) extends ListSeq
case object EmptyList extends ListSeq
object ListSeqExample extends App {
val seq1 = ListPair(1, ListPair(2, ListPair(3, EmptyList)))
val seq2 = EmptyList
assert(seq1.toString == "[1, 2, 3]")
assert(seq2.toString == "[]")
}
This code doesn't compile, the error is:
value elemSequence is not a member of ListPair
case ListPair(hd, tl @ ListPair(_, _)) => s"$hd, ${tl.elemSequence}"
It is not clear to me why this error appears. From what I know, Scala can match on nested fields and bind the fields to a variable - like what is done in case ListPair(hd, tl @ ListPair(_, _))
. But from the error message, it seems that it cannot guess the type of bound object (ListPair
)
Another interesting thing is - if I redefine the ListSeq
in the following way - by removing the elemSequence
method, and all string creation is done in toString
- there is no error:
sealed trait ListSeq {
override def toString: String = this match {
case ListPair(hd, tl @ ListPair(_, _)) => s"$hd, ${tl.toString}"
case ListPair(hd, EmptyList) => s"$hd"
case EmptyList => ""
}
}
I know that the result of toString
will be slightly different (no braces), but it is not the main point here.
Why is there an error in one case, while it happily compiles in another?
It returns an error because elemSequence
is a private
method defined in ListSeq
so it is not visible in ListPair
.
One solution could be to change the visibility to protected
(and add the modifier final so the child classes cannot override
the method):
sealed trait ListSeq {
override def toString: String = s"[$elemSequence]"
final protected def elemSequence: String = {
this match {
case ListPair(hd, tl @ ListPair(_, _)) => s"$hd, ${tl.elemSequence}"
case ListPair(hd, EmptyList) => s"$hd"
case EmptyList => ""
}
}
}
case class ListPair(head: Int, tail: ListSeq) extends ListSeq
case object EmptyList extends ListSeq
object ListSeqExample extends App {
val seq1 = ListPair(1, ListPair(2, ListPair(3, EmptyList)))
val seq2 = EmptyList
assert(seq1.toString == "[1, 2, 3]")
assert(seq2.toString == "[]")
}