Search code examples
scalafunctional-programming

How can I override toString for my List implementation?


Beginner question. I have a minimal List implementation:

sealed trait MyList[+A]

case object Nil extends MyList[Nothing]

case class Cons[+A](head: A, tail: MyList[A]) extends MyList[A]

object MyList {

  def apply[A](as: A*): MyList[A] =
    if (as.isEmpty) Nil
    else Cons(as.head, apply(as.tail: _*))

  private def toString(l: MyList[Any]): String = {
    def f(l: MyList[Any]): String = {
      l match {
        case Nil => "]"
        case Cons(x, Nil) => x.toString + "]"
        case Cons(x, xs) => x.toString + ", " + f(xs)
      }
    }

    "[" + f(l)
  }

  def main(args: Array[String]): Unit = {
    println(toString(MyList(1, 2, 3)))
  }
}

The output of this program is [1, 2, 3]. Is it possible to get the same output without explicitly calling the private method toString()? That is, to make this the default print output when calling println.

I have tried to add this overriding method:

override def toString: String = ...

But I ran into two problems. The first is that it's not actually affecting the output of println, no matter what this overriding method returns (even though println ultimately calls toString). The second is that I don't know how to print the list content without a parameter for the list itself (toString does not take any parameters, so I assume it must reference this, or something else?).


Solution

  • object MyList and trait MyList are 2 different classes in runtime, so whether you put toString in one or the other changes what will be overriden

    sealed trait MyList[+A] {
      override def toString = "x"
    }
    
    case object Nil extends MyList[Nothing]
    
    case class Cons[+A](head: A, tail: MyList[A]) extends MyList[A]
    
    object MyList {
      // ...
     
      override def toString = "y"
    }
    
    println(Nil)
    println(Cons("string", Nil))
    println(MyList)
    

    Additionally case class/case object creates its own override def toString by default so if you want to override toString in both Nil and Cons I would suggest putting final before override def toString in sealed trait MyList[+A] to make sure it is respected.