toString on sorted sets

I’d like a sorted set of items (so maybe TreeSet) - however I’d like to define my own toString() method. There need not be any other changes to the scala collection class.

This is what I found on this:

https://stackoverflow.com/questions/4416885/extend-scala-set-with-concrete-type

However, unlike extending from Set such as described in the answers to the question above, TreeSet is final so I can’t inherit from it.

Would you think about using a custom class and containing a Treeset object with composition? Or would you use an implicit method or use traits?

This seems to me the perfect use case for Showtypeclass usage.

Instead of try extending the class just create an instance of Show for your type.

You can find more info on Show here https://typelevel.org/cats/typeclasses/show.html

Thanks. I’ve come across the same problem again.

I would like to redefine how say perhaps a List is shown, possibly:

"["+ elems.mkString(",") + "]".

Now, using the Cats route, if my list were in a Set or another collection, do I need to redefine how every one of those containers show items? Otherwise they would use their default toString e.g. a set with a list would revert to Set(List(1,2,3))?

You’ll have to forget about #toString() and go all in on Show. The tricky part with using multiple/mixed implicit declarations for the same type is to get the scoping right (if “right” is even possible).

import cats._

trait CustomShowImplicits extends syntax.AllSyntax with instances.AllInstances {
  implicit def customListShow[T : Show]: Show[List[T]] =
    (ts: List[T]) => ts.mkString("[", ", ", "]")

  def printlnS[T : Show](t: T): Unit = println(t.show)
}

object CustomShowImplicits extends CustomShowImplicits

object CollShow extends App {
  import CustomShowImplicits._
  printlnS(Set(List(1,2,3))) // Set([1, 2, 3])
}

Thanks - so if you redefined a Set and put some showable lists in, would you have to recursively call show on the items? I seem to need to but this wipes the type and converts them to Strings…

implicit def customSetShow[T : Show]: Show[Set[T]] =
(ts: Set[T]) => ts.mkString("{", ", ", “}”) // {List(1, 2, 3)}

vs

implicit def customSetShow[T : Show]: Show[Set[T]] =
(ts: Set[T]) => ts.map(_.show).mkString("{", ", ", “}”) // {[1, 2, 3]}

?
(and when doing interactive REPL work do you override def toString to call = this.show?)

I’ve found this from Cats:

  implicit def catsStdShowForList[A: Show]: Show[List[A]] =
    new Show[List[A]] {
      def show(fa: List[A]): String =
        fa.iterator.map(_.show).mkString("List(", ", ", ")")
    }

Perhaps I overwrite this?

Yes, indeed, sorry - I probably shouldn’t post that late… I failed to follow my own advice and go all in on Show. :roll_eyes:

implicit def customListShow[T : Show]: Show[List[T]] =
  (ts: List[T]) => ts.map(_.show).mkString("[", ", ", "]")

implicit def customSetShow[T : Show]: Show[Set[T]] =
  (ts: Set[T]) => ts.map(_.show).mkString("{", ", ", "}")

How/where would you override #toString()?

In the REPL, you’ll explicitly have to invoke #show(), unless your REPL allows to override the standard rendering based on #toString(). The standard Scala REPL probably doesn’t support this, no idea whether ammonite does.

You already do, kind of - customListShow has precedence over catsStdShowForList for implicit resolution.

So would it be correct to say since inbuilt types have a toString method already, an implicit toShow wouldn’t work (say if you wanted to redefine Option formatting) e.g. unless its called from another type which calls show on its objects?

  implicit def myShowForOption[A: Show]: Show[Option[A]] =
    new Show[Option[A]] {
      def show(opt: Option[A]): String = opt match {
        case Some(a) => "newformatSome(" + a.show + ")"
        case None => "newformatNone"
      } 
    }

Every reference type, builtin or custom, has #toString already. #toString() and Show simply are separate worlds.

You can add myShowForOption to CustomShowImplicits, and it will work just like the List and Set instances, “shadowing” the default instance provided by cats.

The catch just is that #show() needs to be used to trigger the mechanism. Which usually shouldn’t be a big deal in your own code, but it can be cumbersome working with “frameworks” that rely on #toString() for rendering and cannot be configured otherwise, like the REPL, or logging, for example.

I have two quick follow on qs, please.

i) as a style question: would you still use == on items you know are safe, e.g. if you were defining an eq for a custom class and were comparing strings, you would use == rather than ===?

ii) I’ve implemented an Eq for a custom class BigClass:

> implicit def myBigClassEq: Eq[BigClass] = new Eq[BigClass] {
>     def eqv(bc1: BigClass,bc2:BigClass): Boolean = {
>       bc1.embeddedList === bc2.embeddedList
>     }
>   }
> 
>   implicit def myListEq[A: Eq]: Eq[List[A]] = new Eq[List[A]] {
>     def eqv(l1: List[A], l2: List[A]): Boolean = {
>       l1.length == l2.length && l1.zip(l2).forall { case (l,r) => l === r }     
>     }
>   }

However, it says === is not a member of List[BigClass].

Is this because there are two levels of implicit searching?

(I imported cats._, cats.data._, and cats.implicits._ but I couldn’t get a default List Eq in place).

i) It depends, but mostly I’d try to stick to the Eq domain. So far I have no experience with using Eq consistently across a code base, though.
ii) Just providing an Eq instance for your custom class should work fine. Eq[List] should be pulled from catsKernelStdEqForList. If this doesn’t work, maybe provide a complete code example here - preferably with a rather SmallClass and without the leading >.