The Show Typeclass in Scala
Alex Westphal · 04 Sep 2014One extremely useful typeclass that Haskell provides in the bases package is the
Show typeclass. Unfortunately the Scala standard
standard libraries provide nothing of the sort so you have to resort to using Java’s Object.toString()
, yuck.
Fortunately scalaz provides us a nice implementation, so we’re going to look how how
it’s works.
The Typeclass
The show typeclass in Haskell looks roughly like the following:
class Show a where
show :: a -> String
Translated into Scala that looks like:
trait Show[A] {
def show(a: A): String
}
Defining Instances
But a typeclass is useless without instances, lets declare one:
// Given: class Foo(val x: Int)
implicit val showFoo = new Show[Foo] {
def show(a: Foo) = "Foo[" + a.x + "]"
}
This is rather verbose for something so simple. Can it be made more succinct? Yes it can. We’ll add a utility method to to the companion object that creates shows instances from functions:
object Show {
def show[A](f: A => String): Show[A] = new Show[A] {
def show(a: A): String = f(a)
}
}
The instance declaration for Foo is then simply:
implicit val showFoo = Show.show( f => "Foo[" + f.x + "]" )
In a lot of case we can simply reuse the implementation provided by java’s toString, so let’s add a utility method for doing that:
object Show {
⋮
def showA[A]: Show[A] = new Show[A] {
def shows(f: A): String = f.toString
}
}
Then an instance for Int is:
implicit val showInt = SHow.showA[Int]
Using the Typeclass
How do we use our lovely typeclass? Implicits and context bounds:
def bar[A: Show](a: A): String = implicitly[Show[A]].show(a) + ".bar"
That call to implicitly
is kinda ugly. Lets add another utility method to the companion object:
object Show {
⋮
@inline def apply[A](implicit A: Show[A]): Show[A] = A
}
Now rather than implicitly[Show[A]]
we can just use Show[A]
More Complex Instances
What about Tuples and List? Actually they’re pretty easy too, we just need to swap the implicit val instance for a parametric def:
implicit def showTuple1[A: Show]: Show[(A)] = Show show {
case (a) => "(" + Show[A].show(a) + ")"
}
implicit def showTuple2[A: Show, B: Show]: Show[(A,B)] = Show show {
case (a,b) => "(" + Show[A].show(a) + "," + Show[B].show(b) + ")"
}
⋮
Better Syntax
Having to call Show[A].show(a)
is still a bit clunky, what if we could reduce it to a.show
? We can do this by
defining a ShowOps trait with appropriate implicit conversions:
trait ShowOps[A] {
def self: A
implicit def S: Show[A]
def show = S.show(self)
}
implicit def ToShowOps[A: Show](a: A) = new ShowOps[A] {
def self = a;
implicit def S: Show[A] = implicitly[Show[A]]
}
Now use is really easy:
def bar[A: Show](a: A): String = a.show + ".bar"
Scalaz
We now pretty much have the scalaz.Show implementation except that scalaz uses a Cord rather than a String, for performance reasons. I plan to explore this in a latter post.