Question

In TraversableOnce, there is a sum method 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
  }
}
Was it helpful?

Solution

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.

OTHER TIPS

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.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top