开发者

How to make the type checking at compile time?

开发者 https://www.devze.com 2023-01-30 08:39 出处:网络
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 chec

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.

0

精彩评论

暂无评论...
验证码 换一张
取 消