I’d like to define a trait with some properties which have a well defined relationship – for example’s sake, let’s say that a * b = c. The idea is that implementations of this trait can provide two of these and have an accessor for the the third property derived automatically.
(This is the same feature as Haskell’s type classes, if I remember those correctly, where if you defined (I don’t remember Haskell’s type classes correctly.)< from Ord, >= would be implemented as ! . < – though you could define any subset of functions so long as the remainder could be inferred.)
The naïve approach actually works fairly well:
trait Foo {
// a * b = c
def a: Double = c / b
def b: Double = c / a
def c: Double = a * b
}
class FooOne(override val a: Double, override val b: Double) extends Foo
class FooTwo(override val a: Double, override val c: Double) extends Foo
Here, implementations FooOne and FooTwo are complete implementations of Foo, and behave as expected. So far, so good; this approach does allow classes to define two of the properties and get the third one “for free”.
However, things start to look less rosy if one defines a third class:
class FooFail(override val a: Double) extends Foo
This compiles just fine – however, it will cause a stack overflow if its b or c methods are ever evaluated.
So the naïve approach gives the inference aspect of Haskell’s type class approach, but we don’t have compile-time safety. What I’d like is for the compiler to complain if less than two of the methods are defined by implementing classes. Clearly the current syntax isn’t sufficient here; we need the methods to be considered abstract, albeit with a default implementation which can be used if and only if the dependent methods are non-abstract.
Does Scala expose appropriate semantics to define this? (I don’t have a problem if there’s a somewhat roundabout way of defining it, similar to union types, since I’m not aware of any first-class support for this in the language).
If not, I’ll make do with the naïve approach and just define and test my classes carefully. But I really think this is something that the type system should be able to catch (after all – it’s not Ruby. :)).
Use implicits: