Возможно ли безопасное с типом response_to в scala? - PullRequest
4 голосов
/ 08 августа 2010

Использование конструкции case для безопасного ввода типов легко в scala. Следующий код гарантирует, что square вызывается только для объектов соответствующего типа.

class O
class A extends O {
    def square(i: Int):Int = { i*i }
}
class B extends O {
    def square(d: Double):Double = { d*d }
}
class C extends O {}

def square(o: O) = o match {
    case a:A => print(a.square(3))
    case b:B => print(b.square(3.0))
    case c:C => print(9)
    case _ => print("9")
}

С другой стороны, бывают случаи, когда не так просто использовать информацию о типе для приведения и достаточно просто проверить наличие {def square(Int): Int}. Есть ли в scala конструкция, позволяющая сделать что-то похожее на

def square(o: O) = o match {
    case a:{def square(Int):Int} => print(a.square(3))
    case b:{def square(Double):Double} => print(b.square(3.0))
    case _ => print("9")
}

Используя неявные параметры доказательств, можно определять методы в зависимости от доступности других методов. Можно ли также вызывать их только тогда, когда они определены?

Ответы [ 2 ]

5 голосов
/ 08 августа 2010

Структурная типизация выражает доступность элементов, не связанных с наследованием, поэтому, если вы хотите, чтобы метод принимал только значения, которые содержат определенный метод, скажем, def square(i: Int): Int, вы используете эту запись:

class Squaring {
  type Squarable = { def square(i: Int): Int }
  def squareMe(s: Squarable): Int = s.square(17)
}

class CanSquare { def square(i: Int) = i * i }

val cs1 = new CanSquare
val s1 = new Squaring

printf("s1.squareMe(cs1)=%d%n", s1.squareMe(cs1))


s1.squareMe(cs1)=289

Вы должны знать, что структурная типизация реализована с помощью отражения, но в Scala 2.8 информация об отражении кэшируется на сайте вызова для каждого класса в отдельности (фактические классы предоставленных значений).

1 голос
/ 10 августа 2010

Кажется, что классы типов являются стандартом применения операции ко многим различным типам.Это не поиск методов во время выполнения (ну, это возможно, но не чистый шаблон), но он может обеспечить то, что вы хотите.

trait Numeric[T] {
  def times(x :T, y : T) : T
}

object Numeric {
  implicit val doubleNumeric = new Numeric[Double] {
    def times(x : Double, y : Double) = x*y
  }
  implicit val intNumeric = new Numeric[Int] {
    def times(x : Int, y : Int) = x*y
  }
}

def square[A : Numeric](x : A) = implicitly[Numeric[A]].times(x,x)

Если вы делаете это в scala REPL, убедитесь, чтообъект Numeric является истинным сопутствующим объектом для черты Numeric.Вы можете сделать это, обернув объявление в другой объект, скажем, tmp, а затем импортировав tmp ._.

Далее, просто вызовите квадрат с другими значениями:

scala> square(2)       
res6: Int = 4

scala> square(4.0)
res7: Double = 16.0

Scala фактически предоставляетКласс числового типа для использования в числовых вычислениях, см .: http://www.scala -lang.org / api / current / scala / math / Numeric.html

Я также написал статью о типеШаблон класса в Scala и методы его использования для адаптации нескольких API или нескольких отправок здесь: http://suereth.blogspot.com/2010/07/monkey-patching-duck-typing-and-type.html

Если вы ищете Google для «класса типа Scala», вы должны увидеть много информации.

Часть # 2 - фактический response_to?

Если вы действительно хотели response_to в scala, вы в некотором роде SOL.Это потому, что response_to действительно является динамической концепцией.Вы определяете метод для класса, который будет вызываться, если вы попытаетесь вызвать метод для класса, который не существует.Scala не абстрагируется от вызовов методов, как это делают некоторые динамические языки JVM.Это означает, что нет никаких перехватов в вызовах методов, которые вы можете перехватывать и взаимодействовать.Лучшее, что вы можете сделать, - это форма адаптации интерфейса или какой-то аспектно-ориентированный хук после компиляции, чтобы переписать для вас байт-код.

Есть одна волшебная часть, которую мы можем использовать,и он также используется в некоторых средах AOP: Динамический прокси.

scala> object AllPowerfulProxy extends InvocationHandler {                      
     | def invoke(proxy : AnyRef, m : Method, args : Array[AnyRef]) : AnyRef = {
     | println(" You really want to call " + m.getName + "?")
     | null // Maliciously Evil!
     | }
     | }
defined module AllPowerfulProxy

scala> def spawn[A : Manifest] : A = {
     |   val mf = implicitly[Manifest[A]]        
     |   java.lang.reflect.Proxy.newProxyInstance(mf.erasure.getClassLoader,
     |                                            Array(mf.erasure),
     |                                            AllPowerfulProxy).asInstanceOf[A]
     | }
spawn: [A](implicit evidence$1: Manifest[A])A

Теперь мы можем использовать его для порождения объектов в любом интерфейсе.Давайте посмотрим, что мы можем сделать:

scala> val x = spawn[TestInterface]
 You really want to call toString?
java.lang.NullPointerException
    at scala.runtime.ScalaRunTime$.stringOf(ScalaRunTime.scala:259)

Вы видите, что мы там сделали?Когда REPL пытается вызвать toString для результата выражения, он вызывает его на нашем динамическом прокси.Поскольку прокси является заполнителем, фактический вызов делегируется нашему классу AllPowerfulProxy, как печатается сообщение: «Вы действительно хотите вызвать toString?».REPL тогда достигает нулевого возврата и выдает исключение.Видите ли, использование динамических прокси перемещает ошибки во время выполнения, поэтому вам нужно быть очень осторожным при создании объектов и возвращении правильных типов.В зависимости от сложности вашей системы вы должны также беспокоиться о загрузчиках классов.Если вы когда-нибудь получите ClassCastException от Foo для Foo, то вы знаете, что попали в ад загрузчика классов.

В любом случае, если у вас есть другие вопросы по динамическим прокси, не стесняйтесь их задавать.В статически типизированных языках вам, вероятно, лучше использовать классы типов и переходить к разработке шаблонов, используя их вместо тех, которые используют response_to.(Вы будете удивлены, что вы можете сделать с помощью классов типов и системы типов).

...