object SalaryComputation {
trait Source {
type E
val salaried: List[E]
val salCalculator: (E => Double)
}
class Context {
val payees = new ArrayBuffer[Source]
def addSource[T](p: List[T], salCalc: (T => Double)) = {
payees += new Source {
type E = T
val salaried = p
val salCalculator = salCalc
}
}
}
def compute = {
val emps = List[Employee](
//..
)
val cemps = List[Contractor](
//..
)
val c = new Context()
c.addSource(emps, (e: Employee) => (e.monthlyBasic + e.allowance - e.monthlyBasic * 0.3))
c.addSource(cemps, (e: Contractor) => (e.hourlyRate * 8 * 20) * 0.7)
c.payees.map(p => p.salaried.map(p.salCalculator(_))).foreach(println)
}
}
and the workers are modeled as case classes ..
case class Employee(id: Int, name: String, monthlyBasic: Int, allowance: Int)
case class Contractor(id: Int, name: String, hourlyRate: Int)
case class DailyWorker(id: Int, name: String, dailyRate: Int, overtimeRate: Int)
The salary computation part has a separate
Source
component which abstracts the data source and salary computation strategy for one particular class of worker. I can have multiple classes of workers being fed into the salary computation component through multiple instances of the Source
component. And this aggregation is managed as the Context
of the computation. Have a look at the compute method that adds up Source
s into the context and builds up the collection for total computation of salaries.In the above implementation, the trait
Source
abstracts the type of the worker over the collection for which salary will be computed and the salary computation strategy. And both of them are injected when new instances of Source
are being created in method addSource()
. The client interface to the abstraction (in method compute
) looks nice and DRY and it works as per expectation.But what about Existential Types ?
In one of the recent releases, Scala has introduced existential types, which offers hiding (pack) of concrete types from the user and allows them to manipulate objects using a given name (unpack) and bounds. Keeping aside all type theoretic foundations behind existential types, I found it equally convenient to implement the above model using existential types instead of abstract type members. Here it goes ..
object SalaryComputation {
case class Source[E](salaried: List[E], salCalculator: (E => Double))
class Context {
val payees = new ArrayBuffer[Source[T] forSome { type T }]
def addSource[T](p: List[T], salCalc: (T => Double)) = {
payees += new Source[T](p, salCalc)
}
}
def compute = {
val emps = List[Employee](
//..
)
val cnts = List[Contractor](
//..
)
val c = new Context()
c.addSource(emps, (e: Employee) => (e.monthlyBasic + e.allowance - e.monthlyBasic * 0.3))
c.addSource(cnts, (e: Contractor) => (e.hourlyRate * 8 * 20) * 0.7)
def calc[T](c: Source[T]) = {
c.salaried.map(c.salCalculator(_))
}
c.payees.map(calc(_)).foreach(println(_))
}
}
The Scala book by Artima mentions that existential types have been introduced in Scala mainly for interoperability between Scala and Java's wild card types and raw types. But as I found above, they offer all benefits of type abstraction as abstract type members. Ignoring the stylistic issues, is there any reason to choose one approach over the other in the above implementations ?
4 comments:
My gosh, how do you find time to write so much about Scala? Do you use it a _lot_ at work?
Hi,
the Artima book shows how to use type members to enforce static constraints such as, when animals eat food, cows are only allowed to eat grass (btw: the book also has a very convincing currency example)
do you think you can also elegantly impose such static constraints using existential types?
Luc
Debasish,
See what M. Odersky says in a comment on this blog post:
http://www.drmaciver.com/2008/03/existential-types-in-scala/
Cheers
@german: Yeah .. I have seen in many places that generally abstract type members are recommended over existentials. But yet to find any convincing arguments for that. The artima book also mentions that existentials are mainly targetted for interoperating with Java generics. However, I have seen some use cases where I think existentials will make a better cut.
Post a Comment