I am modeling units of time through case classes. OOP Hierarchy
sealed abstract class Time
├── case class Second(Double)
└── case class Minute(Double)
I have a few requirements:
1) Arithmetic operations must always return the type of the left-hand-side operand; a Second
+ T
is always a Second
, and vice versa.
val a = Second(5)
val b = Minute(3)
a + b // Second(185.0)
b + a // Minute(3.0833)
1.1) This should also apply to scenarios when an operand’s type is only known vaguely as Time
. A Time
+ T
will evaluate as a vague Time
type also, though the runtime value
will be a specific concrete subclass of course. While a T
+ Time
will evaluate as a T
specificially.
val general: Time = Second(10)
val specific = Second(5)
general + specific // Second(15): Time
specific + general // Second(15): Second
2) Comparison operations must work for all permutations of types
val a = Second(5)
val b = Minute(5)
a < b // true
b > a // true
2.1) including scenarios of vague Time
operands
val general: Time = Second(5)
val specific = Minute(10)
general < specific // true
specific > general // true
general <= general // true
3) Standard library methods like .sum
and .max
must work for sequences
The known type will evaluate as the T
in Seq[T]
, and the runtime value type will be the type of the first element.
Seq(Second(5), Second(3)).sum // Second(8): Second
Seq(Second(5), Minute(3)).sum // Second(185.0): Second | Minute
Seq(Minute(5), Second(3)).sum // Minute(3.0833): Second | Minute
Seq[Time](Second(5), Second(3)).sum // Second(8): Time
Seq(Second(5), Minute(1)).max // Minute(1): Second | Minute
I would prefer not having to implement these methods myself if possible. Is there a more automatic technique to get stdlib methods willing to operate on my own quantity types?
I also plan to support more types, Hour
, Millisecond
, Microsecond
and so on, with operations between all of them supported. So, generalized and DRY solutions are important to avoid an unfeasibly large number of permutations.
Thank you.
Scastie: Scastie - An interactive playground for Scala.
(But you may diverge if you think there are ways my current implementation can be improved)