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?
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)
.
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
}
}
© 2022 - 2024 — McMap. All rights reserved.
implicit class
syntax to simplify this a little. – Heisser