There may be a more elegant solution, but you can achieve this by moving the type constraint to a method, rather than a type declaration:
trait Foo { type Bar val lowerBound: Bar val upperBound: Bar def foo(bar: Bar)(implicit ev: Bar => Ordered[Bar]) = { bar >= lowerBound && bar <= upperBound } }
Then your FooImpl works as it is:
class FooImpl extends Foo { type Bar = Int val lowerBound = 0 val upperBound = 5 }
In REPL:
scala> new FooImpl() res0: FooImpl = FooImpl@2dbbec72 scala> res0.foo(3) res1: Boolean = true scala> res0.foo(7) res2: Boolean = false
The disadvantage here is that this attribute can be extended with unordered types (although foo cannot be called in this case):
class A // not Ordered class BrokenFoo extends Foo { type Bar = A val lowerBound = new A val upperBound = new A }
Alternatively, you can keep the requirement at the class level (and therefore stop any BrokenFoo creation) as follows, but FooImpl should change a bit:
trait Foo { type Bar implicit val baz: Bar => Ordered[Bar] val lowerBound: Bar val upperBound: Bar def foo(bar: Bar) = { bar >= lowerBound && bar <= upperBound } } class FooImpl extends Foo { type Bar = Int val baz = implicitly[Bar => Ordered[Bar]] val lowerBound = 0 val upperBound = 5 }
This problem looks like this: the view or context boundaries should be applicable, but, unfortunately, it seems that you cannot use them in type declarations or in general type parameter parameters.
source share