typeclass - Problems generalizing Scala type classes -


while working on scala project used type class pattern, ran appears serious problem in how language implements pattern: since scala type-class implementations must managed programmer , not language, variable belonging type-class can never become annotated parent type unless type-class implementation taken it.

to illustrate point, i've coded quick example program. imagine trying write program handle different kinds of employees company , print reports on progress. solve type-class pattern in scala, might try this:

abstract class employee class packer(boxespacked: int, cratespacked: int) extends employee class shipper(trucksshipped: int) extends employee 

a class hierarchy modelling different kinds of employees, simple enough. implement reportmaker type-class.

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) } } 

that's , good, , can write kind of roster class might 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 works. now, our type-class, can pass either packer or shipper object reportandadd method, , print report , add employee roster. however, writing method attempt print out report of every employee in roster impossible, without explicitly storing rm object gets passed reportandadd!

two other languages support pattern, haskell , clojure, don't share problem, since deal problem. haskell's stores mapping datatype implementation globally, 'with' variable, , clojure same thing. here's quick example works in clojure.

    (defprotocol reporter       (report [this] "produce string report of 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 #(println (report %)) roster)))      (def steve (packer. 10 5))     (def billy (shipper. 5))      (def roster [steve billy])      (report-roster roster) 

apart rather nasty solution of turning employee list type list[(employee, reportmaker[employee]), scala offer way solve issue? , if not, since scala libraries make extensive use of type-classes, why hasn't been addressed?

the way you'd typically implement algebraic data type in scala case classes:

sealed trait employee case class packer(boxespacked: int, cratespacked: int) extends employee case class shipper(trucksshipped: int) extends employee 

this gives pattern extractors packer , shipper constructors, can match on them.

unfortunately, packer , shipper distinct (sub)types, part of pattern of encoding algebraic data type in scala disciplined ignoring this. instead, when distinguishing between packer or shipper, use pattern matching in haskell:

implicit object employeereportmaker extends reportmaker[employee] {   def printreport(e: employee) = e match {     case packer(boxes, crates) => // ...     case shipper(trucks)       => // ...   } } 

if have no other types need reportmaker instance, perhaps type class isn't needed, , can use printreport function.


Comments

Popular posts from this blog

c# - Send Image in Json : 400 Bad request -

jquery - Fancybox - apply a function to several elements -

An easy way to program an Android keyboard layout app -