Given the code below the method foo
should compare operator-wise a given parameter bar
with the lowerBound
and upperBound
all being of the same abstract type Bar
.
trait Foo {
type Bar <: Ordered[Bar]
val lowerBound: Bar
val upperBound: Bar
def foo(bar: Bar) = bar >= lowerBound && bar <= upperBound
}
This way the trait Foo
can be defined. The problems start with the below concrete class FooImpl
.
class FooImpl extends Foo {
type Bar = Int
val lowerBound = 0
val upperBound = 5
}
I understand that scala.Int
isn't implementing what scala.runtime.RichInt
does, effectively scala.math.Ordered[Int]
. Defining type Bar
as RichInt
instead neither works as it does not conform to scala.math.Ordered[RichInt]
. My third attempt to define type Bar
as Ordered[Ord]
where Ord
is declared as type Ord
and defining it in FooImpl
as Int
also did not work.
How would a possibly close solution look like?
There may be a more elegant solution, but you can achieve this by moving the restriction on the type to the method, rather than the 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 you have it:
class FooImpl extends Foo {
type Bar = Int
val lowerBound = 0
val upperBound = 5
}
From the 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 the trait can be extended with unordered types (although foo
can't be called in that case):
class A // not Ordered
class BrokenFoo extends Foo {
type Bar = A
val lowerBound = new A
val upperBound = new A
} // compiles
new BrokenFoo().foo(new A) // doesn't compile
Alternatively, you can keep the requirement at the class level (and therefore stop anyone creating a BrokenFoo
) as follows, but FooImpl
has to change slightly:
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 feels like view or context bounds should be applicable, but unfortunately it doesn't seem like you can use them either in type
declarations or in generic type parameters on traits.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With