Можно ли добавить метод к встроенному типу в Scala? - PullRequest
9 голосов
/ 18 ноября 2008

Я хотел бы добавить метод к встроенному типу (например, Double), чтобы я мог использовать оператор infix. Это возможно?

Ответы [ 2 ]

18 голосов
/ 18 ноября 2008

Да и нет. Да, вы можете сделать так, чтобы выглядело , как будто вы добавили метод к double. Например:

class MyRichDouble(d: Double) {
  def <>(other: Double) = d != other
}

implicit def doubleToSyntax(d: Double) = new MyRichDouble(d)

Этот код добавляет ранее недоступный оператор <> к любому объекту типа Double. Пока метод doubleToSyntax находится в области видимости, чтобы его можно было вызывать без квалификации, будет работать следующее:

3.1415 <> 2.68     // => true

Часть ответа «нет» проистекает из того факта, что вы на самом деле ничего не добавляете в класс Double. Вместо этого вы создаете преобразование из Double в новый тип, который определяет нужный метод. Это может быть гораздо более мощная техника, чем открытые классы, предлагаемые многими динамическими языками. Это также оказывается полностью безопасным для типов. : -)

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

  • Этот метод не позволяет вам удалить или переопределить существующих методов, просто добавьте новые
  • Неявный метод преобразования (в данном случае doubleToSyntax) обязательно должен находиться в области видимости, чтобы требуемый метод расширения был доступен

Идиоматически, неявные преобразования либо помещаются в одноэлементные объекты и импортируются (например, import Predef._), либо в черты и наследуются (например, class MyStuff extends PredefTrait).

Небольшое отступление: «операторы инфикса» в Scala на самом деле являются методами. Нет никакой магии, связанной с методом <>, который позволяет ему быть инфиксным, парсер просто принимает его таким образом. Вы также можете использовать «обычные методы» в качестве инфиксных операторов, если хотите. Например, класс Stream определяет метод take, который принимает один параметр Int и возвращает новый Stream. Это можно использовать следующим образом:

val str: Stream[Int] = ...
val subStream = str take 5

Выражение str take 5 буквально идентично str.take(5).

1 голос
/ 18 ноября 2008

Эта функция пригодилась для реализации класса, выполняющего оценку ошибок:

object errorEstimation {
  class Estimate(val x: Double, val e: Double) {
    def + (that: Estimate) =
      new Estimate(this.x + that.x, this.e + that.e)
    def - (that: Estimate) =
      new Estimate(this.x - that.x, this.e + that.e)
    def * (that: Estimate) =
      new Estimate(this.x * that.x,
                   this.x.abs*that.e+that.x.abs*this.e+this.e*that.e)
    def / (that: Estimate) =
      new Estimate(this.x/that.x,
                   (this.x.abs*that.e+that.x.abs*this.e)/(that.x.abs*(that.x.abs-that.e)))
    def +- (e2: Double) =
      new Estimate(x,e+e2)
    override def toString =
      x + " +- " + e
  }
  implicit def double2estimate(x: Double): Estimate = new Estimate(x,0)
  implicit def int2estimate(x: Int): Estimate = new Estimate(x,0)

  def main(args: Array[String]) = {
    println(((x: Estimate) => x+2*x+3*x*x)(1 +- 0.1))
    // 6.0 +- 0.93
    println(((x: Estimate) => (((y: Estimate) => y*y + 2)(x+x)))(1 +- 0.1))
    // 6.0 +- 0.84
    def poly(x: Estimate) = x+2*x+3/(x*x)
    println(poly(3.0 +- 0.1))
    // 9.33333 +- 0.3242352
    println(poly(30271.3 +- 0.0001))
    // 90813.9 +- 0.0003
    println(((x: Estimate) => poly(x*x))(3 +- 1.0))
    // 27.037 +- 20.931
  }
}
...