Как сделать проверку типов во время компиляции? - PullRequest
4 голосов
/ 10 декабря 2010

В TraversableOnce существует метод sum, который можно использовать только в том случае, если содержащийся тип равен Numeric (иначе он не будет компилироваться). Интересно, можно ли это использовать в другом случае (чтобы избежать проверки во время выполнения).

В частности, в случае, когда у нас есть две черты A и B. Мы хотим иметь метод f, который можно использовать, только если объект наследует и A, и B. Но нет, если он расширяется только один из них. Я не хочу делать еще один trait AB extends A with B. Я просто хочу быть неспособным использовать f, если не обе черты наследуются.

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) }
  }
}

РЕДАКТИРОВАТЬ: решение, благодаря @ Аарон Новструп

trait Bar extends Base { self =>
  def f(implicit ev: self.type <:< Foo) = {
    //self.asInstanceOf[Foo].g // [1]
    ev(this).g // [2]
  }
}

Теперь в main, b.f не компилируется. Nice

РЕДАКТИРОВАТЬ 2: измененная строка [1] на [2] отражает изменения в ответе @Aaron Novstrup

РЕДАКТИРОВАТЬ 3: без использования self отражают изменения в ответе @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
  }
}

Ответы [ 2 ]

8 голосов
/ 10 декабря 2010

Да, вы можете:

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!")
   }
}

Компилятор сможет предоставить параметр evidence только в том случае, если статический тип объекта (на сайте вызовов) расширяется A.

3 голосов
/ 10 декабря 2010

Зная, что сигнатура суммы

def sum [B >: A] (implicit num: Numeric[B]) : B

Вы, похоже, предполагаете, что числовые типы расширяются Числовой , это не так.На самом деле они неявно преобразуются в Числовой , в случае Int подразумеваемым является scala.math.Numeric.IntIsIntegral , который определяет такие операции, как плюс и раз .

Таким образом, ограничение на то, какие типы A a TraversableOnce [A] .sum разрешены, достигается наличием неявного предоставления необходимых операций.

Это просто краткое объяснение общей работы числовых и типовых классов.Для получения дополнительной информации проверьте источники math.Numeric.XisY, math.Integral и math.Fractional и то, как работают классы типов: неявный-трюк-тип-шаблон-класса и тип-класс-шаблон-шаблона-пример .

...