How to make the type checking at compile time?
In TraversableOnce
, there is a sum
meth开发者_如何学Good that is only usable if the contained type is Numeric
(else it won't compile). I wonder if this is usable for other case (to avoid runtime check).
In particular the case where we have two traits A and B. We want to have a method f
that can be used only if the object inherits both A and B. But not if it extends only one of them. I don't want to make another trait AB extends A with B
. I just want to be unable to use f
if not both traits are inherited.
package com.example
trait Base
trait Foo extends Base {
def g = println("foo bar " + toString)
}
trait Bar extends Base {
/* If this is both Foo and Bar, I can do more */
def f = {
if (!this.isInstanceOf[Foo]) error("this is not an instance of Foo")
this.asInstanceOf[Foo].g
}
}
object Test {
def main(args: Array[String]): Unit = {
object ab extends Foo with Bar
object ba extends Bar with Foo
object b extends Bar
ab.f
ba.f
// I don't want next line to compile:
try { b.f } catch { case e: RuntimeException => println(e) }
}
}
EDIT: solution, thanks to @Aaron Novstrup
trait Bar extends Base { self =>
def f(implicit ev: self.type <:< Foo) = {
//self.asInstanceOf[Foo].g // [1]
ev(this).g // [2]
}
}
Now in main
, b.f
doesn't compile. Nice
EDIT 2: changed line [1] to [2] reflect changes in answer by @Aaron Novstrup
EDIT 3: without using self
reflect changes in answer by @Aaron Novstrup
trait Bar extends Base {
/* If this is both Foo and Bar, I can do more */
def f(implicit ev: this.type <:< Foo) = {
ev(this).g
}
}
Yes, you can:
trait A {
def bar = println("I'm an A!")
}
trait B {
def foo(implicit ev: this.type <:< A) = {
ev(this).bar
println("and a B!")
}
}
The compiler will only be able to supply the evidence
parameter if the object's static type (at the call site) extends A
.
Knowing that the signature of sum is
def sum [B >: A] (implicit num: Numeric[B]) : B
You seem to be assuming that number types extends Numeric, this is not true. Actually they are implicitly converted to Numeric, in the case of Int the implicit used is scala.math.Numeric.IntIsIntegral which define operations like plus and times.
So the restriction on what types of A a TraversableOnce[A].sum is allowed is achieved by the existence of implicit provinding the required operations.
This is just a quick explanation of the overall workings of Numeric and type classes. For more check the sources of math.Numeric.XisY, math.Integral and math.Fractional and how type classes works: implicit-tricks-type-class-pattern and type-class-pattern-example.
精彩评论