While working on a Scala project that used the Type Class template, I came across what seems to be a serious problem in how the language implements the template: since Scala class type implementation should be controlled by the programmer and not the language, any type-type variable , can never be annotated as a parent type, unless its implementation of a type class is taken with it.
To illustrate this point, I encoded a quick sample program. Imagine that you were trying to write a program that could handle various kinds of employees for a company and could print reports on their progress. To solve this problem with a type type template in Scala, you can try something like this:
abstract class Employee class Packer(boxesPacked: Int, cratesPacked: Int) extends Employee class Shipper(trucksShipped: Int) extends Employee
The class hierarchy, which models different types of employees, is quite simple. Now we are implementing a class like ReportMaker.
trait ReportMaker[T] { def printReport(t: T): Unit } implicit object PackerReportMaker extends ReportMaker[Packer] { def printReport(p: Packer) { println(p.boxesPacked + p.cratesPacked) } } implicit object ShipperReportMaker extends ReportMaker[Shipper] { def printReport(s: Shipper) { println(s.trucksShipped) } }
This is good and good, and now we can write some Roster class that might look like this:
class Roster { private var employees: List[Employee] = List() def reportAndAdd[T <: Employee](e: T)(implicit rm: ReportMaker[T]) { rm.printReport(e) employees = employees :+ e } }
So it works. Now, thanks to our class type, we can either pass the wrapper or the sender object to the reportAndAdd method, and it will print the report and add the employee to the list. However, it will be impossible to write a method that tries to print the report of each employee in the list without explicitly storing the rm object, which will be passed to reportAndAdd!
The other two languages that support the template, Haskell and Clojure, do not share this problem, as they deal with this problem. Haskell preserves mapping from a data type to an implementation globally, so it is always "with" a variable, and Clojure basically does the same. Here is a quick example that works great in Clojure.
(defprotocol Reporter (report [this] "Produce a string report of the object.")) (defrecord Packer [boxes-packed crates-packed] Reporter (report [this] (str (+ (:boxes-packed this) (:crates-packed this))))) (defrecord Shipper [trucks-shipped] Reporter (report [this] (str (:trucks-shipped this)))) (defn report-roster [roster] (dorun (map
Besides the rather nasty decision to turn a list of employees into a List [(Employee, ReportMaker [Employee]) type, does Scala offer any way to solve this problem? And if not, since Scala libraries make extensive use of Type-Classes, why wasn't it addressed?