Is it possible to add a method to a built-in type in Scala?
Asked Answered
A

2

11

I would like to add a method to a built-in type (e.g. Double), so that I can use an infix operator. Is that possible?

Archaeology answered 18/11, 2008 at 7:43 Comment(0)
L
19

Yes and no. Yes, you can make it seem like you have added a method to double. For example:

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

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

This code adds the previously-unavailable <> operator to any object of type Double. So long as the doubleToSyntax method is in scope so that it could be invoked without qualification, the following will work:

3.1415 <> 2.68     // => true

The "no" part of the answer comes from the fact that you aren't really adding anything to the Double class. Instead, you are creating a conversion from Double to a new type which does define the method you want. This can be a much more powerful technique than the open-classes offered by many dynamic languages. It also happens to be completely type-safe. :-)

Some limitations you should be aware of:

  • This technique does not allow you to remove or redefine existing methods, just add new ones
  • The implicit conversion method (in this case, doubleToSyntax) absolutely must be in-scope for the desired extension method to be available

Idiomatically, implicit conversions are either placed within singleton objects and imported (e.g. import Predef._) or within traits and inherited (e.g. class MyStuff extends PredefTrait).

Slight aside: "infix operators" in Scala are actually methods. There is no magic associated with the <> method which allows it to be infix, the parser simply accepts it that way. You can also use "regular methods" as infix operators if you like. For example, the Stream class defines a take method which takes a single Int parameter and returns a new Stream. This can be used in the following way:

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

The str take 5 expression is literally identical to str.take(5).

Laryngoscope answered 18/11, 2008 at 7:50 Comment(1)
You can use the implicit class syntax to simplify this a little.Heisser
A
2

This feature came in handy to implement a class performing error estimation:

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
  }
}
Archaeology answered 18/11, 2008 at 8:28 Comment(1)
That's actually kinda neat. :)Kalmia

© 2022 - 2024 — McMap. All rights reserved.