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?
We can define a method's list of parameters in parentheses after its name. After the parameters list, we can provide a return type for the method with the leading colon sign. We then define a method's body after the equals sign. The compiler allows us to skip the curly braces if there is only one statement of code.
A method belongs to an object (usually the class , trait or object in which you define it), whereas a function is by itself a value, and because in Scala every value is an object, therefore, a function is an object. The second def is an object of type Int => Int (the syntactic sugar for Function1[Int, Int] ).
def is the keyword you use to define a method, the method name is double , and the input parameter a has the type Int , which is Scala's integer data type. The body of the function is shown on the right side, and in this example it simply doubles the value of the input parameter a : def double(a: Int) = a * 2 -----
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:
doubleToSyntax
) absolutely must be in-scope for the desired extension method to be availableIdiomatically, 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
}
}
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With