kmeredith (23) [Avatar] Offline
#1
Looking at Listing 4.3, I ran the following in REPL on Scala 2.11.6:

scala> import java.util.Date
import java.util.Date

scala> sealed trait TransactionType
defined trait TransactionType

scala> case object DR extends TransactionType
defined object DR

scala> case object CR extends TransactionType
defined object CR

scala> sealed trait Currency
defined trait Currency

scala> case object USD extends Currency
defined object USD

scala> case object JPY extends Currency
defined object JPY

scala> case object AUD extends Currency
defined object AUD

scala> case object INR extends Currency
defined object INR

scala> case class Money(m: Map[Currency, BigDecimal]) {
     | }
defined class Money

scala> case class Transaction(txid: String, accountNo: String, date: Date,
     | amount: Money, txnType: TransactionType, status: Boolean)
defined class Transaction


However, I'm a bit confused at the following trait's type parameters.

scala> trait Analytics[Transaction, Balance, Money] {
     |   def maxDebitOnDay(txns: List[Transaction]): Money
     |   def sumBalances(bs: List[Balance]): Money
     | }
defined trait Analytics


I can define the following silly Analytics instance:

scala> new Analytics[Int, Int, Int] {
     |   override def maxDebitOnDay(txns: List[Int]) = 100
     |   override def sumBalances(bs: List[Int]) = 100
     | }
res1: Analytics[Int,Int,Int] = $anon$1@7c2dfa2

scala> res1.maxDebitOnDay(List(1,2,3))
res2: Int = 100


These type parameters don't appear to be meaningful, i.e. strongly typed, given that example that I provided.

Perhaps I'm missing something? Should they be sub-type constraints or typeclasses (I look forward to your discussion of them)?
Debasish Ghosh (116) [Avatar] Offline
#2
In the example, the trait Analytics is parametric in 3 types. To make a concrete type, you need to fill in the placeholders with meaningful concrete types. In the example, the generic types are not bounded - hence you can put arbitrary types in there. In fact mocks are often set up that way for testing. The idea is to apply proper semantics with the types that you fill up with.

Another alternative would have been to define some protocols for each of the types, which the concrete types must honor, e.g. we can have a TransactionProtocol and we can make the generic type Transaction <: TransactionProtocol in the algebra of the trait. That would have constrained the parametric polymorphism of the trait that we are defining. But if you would like to apply such constraints that's perfectly valid (constrained polymorphism). Leaving it open is also a valid option - leave it up to the user's imagination to provide valid types.

One thing is important to remember - when we define a trait (not the implementation), we are just defining the algebra. It's up to the implementor to provide the semantics.

Thanks.