Override toString behavior for Option[_] in scala

1.4k views Asked by At

I would prefer to see just the value of the Option (if it were not None) , instead of the following additional Some() noise:

List((Some(OP(_)),Some(share),3), (Some(OP(D)),Some(shaara),4), (Some(OP(I)),Some(shaaee),4))

Now, I could write a method that handles this for List[Option[_]] .. But there are many other structures in which Options appear - so this approach of addressing explicitly each one is cumbersome.

Due to implicits having lower precedence the following code simply gets ignored:

  implicit def toString(myopt : Option[_]) = if (myopt == None) "None" else myopt.get

The concern is that - although implementing for example a toString(List[Option_]]) method that handles this in the desired manner, that is still a one-off. What about a

Map[Option,Option]  =>     def toString(Map[Option,Option]) = { .. }

It seems We would still need to implement an explicit toString() for each collection type..

3

There are 3 answers

0
WestCoastProjects On BEST ANSWER

Following takes care of the cases that came to mind:

  def show(obj: Any) : String = {
    obj match {
      case o: Option[_] =>
        if (o == None) {
          "<none>"
        } else {
          show(o.get)
        }
      case i: Iterable[_] =>
        i.map(show).mkString("[", ",", "]")
      case m: Map[_, _] =>
        m.map {
          case (a, b) => List(show(a), show(b)).mkString(":")
        }.mkString("{", ",", "}")
      case e: Enumeration =>
        e.toString
      case c : Product if !c.getClass.getMethods.map(_.getName).contains("copy$default$2") =>
        c.toString
      case t: Product =>
        t.productIterator.map(a => show(a)).mkString("(", ",", ")")
      case _ =>
        if (obj.isInstanceOf[AnyRef])
          obj.asInstanceOf[AnyRef].toString
        else
          "" + obj
    }
  }

I opted to code this up to avoid adding scalaz dependency (assuming their Show class supports similar feature)

3
senia On

I guess you can't override this behavior for toString, but you could use shows (and show) methods from scalaz. You could override behavior of these methods:

import scalaz._, Scalaz._
val l = List(1.some, none, 3.some, 4.some, none, 6.some)

l.shows
// res0: String = [Some(1),None,Some(3),Some(4),None,Some(6)]

implicit def optionShow[T: Show]: Show[Option[T]] =
  Show.show{ _.map{_.show}.getOrElse(Cord("<none>")) }

l.shows
// res1: String = [1,<none>,3,4,<none>,6]

It works for all types with Show:

1.some.node(none.node(2.some.leaf)).drawTree
// 1
// |
// `- <none>
//    |
//    `- 2

Map(1.some -> 2.some, none[Int] -> 3.some).shows
// Map[1->2, <none>->3]
2
Kigyo On

Well i would just write a Wrapper similar to your implicit.

class OW[T](val option : Option[T]) {
  override def toString = if (option.isEmpty) "None" else option.get.toString
}

Then when i want the pretty toString, i would just map any collection to instances of OW.

println(List(Some(3), Some("Hello"), None).map(new OW(_)))

which prints: List(3, Hello, None)

Don't really see a better way.