operator overloading in scala
DESCRIPTION
I gave this presentation on January 14, 2010 to the Atlanta Scala user group. It covers Scala's implementation of operator overloading, as well as touching on implicit conversions.TRANSCRIPT
![Page 1: Operator Overloading In Scala](https://reader033.vdocuments.net/reader033/viewer/2022051313/547ec1a2b4af9f9b158b57b1/html5/thumbnails/1.jpg)
OPERATOR OVERLOADING
IN SCALA
Joey Gibson
![Page 2: Operator Overloading In Scala](https://reader033.vdocuments.net/reader033/viewer/2022051313/547ec1a2b4af9f9b158b57b1/html5/thumbnails/2.jpg)
First Things First
Working with Java since 1996
Playing with Scala for not-quite-a-year
Originally a blog post on joeygibson.com
See the original at http://bit.ly/wNvAl
Based on example from Programming Scala, by
Venkat Subramaniam
![Page 3: Operator Overloading In Scala](https://reader033.vdocuments.net/reader033/viewer/2022051313/547ec1a2b4af9f9b158b57b1/html5/thumbnails/3.jpg)
What Is Operator Overloading?
A language facility that allows developers to define
behavior for symbols like +, -, / for their own
classes, mimicking built-in operators
C++, Smalltalk, Ruby, lots of others have it
Java does not have it
Except + for String concatenation
BigDecimal, BigInteger, etc. would be nicer to use with
operators
![Page 4: Operator Overloading In Scala](https://reader033.vdocuments.net/reader033/viewer/2022051313/547ec1a2b4af9f9b158b57b1/html5/thumbnails/4.jpg)
The Basics
Technically, Scala does not have operator
overloading
Operators are just methods
Any method taking 0/1 argument can be called as an
operator
foo doSomething “x” is the same as foo.doSomething(“x”)
foo + 23 is the same as foo.+(23)
You can define both binary and unary operators
Only +, -, ~ and ! can be used as unary operators
As with binary operators, it’s just a method and
-foo is the same as foo.unary_-
![Page 5: Operator Overloading In Scala](https://reader033.vdocuments.net/reader033/viewer/2022051313/547ec1a2b4af9f9b158b57b1/html5/thumbnails/5.jpg)
What About Precedence?
Scala looks at the first character of the operator
name
All other special chars
* / %
+ -
:
= !
< >
&
^
|
All letters
All assignment operators
![Page 6: Operator Overloading In Scala](https://reader033.vdocuments.net/reader033/viewer/2022051313/547ec1a2b4af9f9b158b57b1/html5/thumbnails/6.jpg)
Operator Naming
Typically, you would use arithmetic operator
symbols, but you don’t have to
Since operators are just methods, you can use non-
standard characters
Δ, λ, γ, Ω, etc.
Neat/useful, but use sparingly, unless it makes your
code easier to read and/or maintain
If the last character in an operator name is a colon,
that method associates to the right
![Page 7: Operator Overloading In Scala](https://reader033.vdocuments.net/reader033/viewer/2022051313/547ec1a2b4af9f9b158b57b1/html5/thumbnails/7.jpg)
Complex.scala
package com.joeygibson.oopres
class Complex(val real: Int, val imaginary: Int) {
def +(operand: Complex): Complex = {
new Complex(real + operand.real, imaginary + operand.imaginary)
}
def *(operand: Complex): Complex = {
new Complex(real * operand.real - imaginary * operand.imaginary,
real * operand.imaginary + imaginary * operand.real)
}
override def toString() = {
real + (if (imaginary < 0) "" else "+") + imaginary + "i"
}
}
![Page 8: Operator Overloading In Scala](https://reader033.vdocuments.net/reader033/viewer/2022051313/547ec1a2b4af9f9b158b57b1/html5/thumbnails/8.jpg)
ComplexTest.scala
package com.joeygibson.oopres
import org.junit.runner.RunWith
import org.scalatest.FlatSpec
import org.scalatest.matchers.ShouldMatchers
import org.scalatest.junit.JUnitRunner
@RunWith(classOf[JUnitRunner])
class ComplexTest extends FlatSpec with ShouldMatchers {
"A Complex" should "sum up to 4-9i" in {
val c1 = new Complex(1, 2)
val c2 = new Complex(2, -3)
val c3 = c1 + c2
val res = c1 + c2 * c3
res.toString should equal ("4-9i")
}
}
![Page 9: Operator Overloading In Scala](https://reader033.vdocuments.net/reader033/viewer/2022051313/547ec1a2b4af9f9b158b57b1/html5/thumbnails/9.jpg)
Interesting Naming Examples
package com.joeygibson.oopres
import org.apache.commons.lang.StringUtils
class Fragment(val text: String) {
override def toString = text
def Δ(other: Fragment): Fragment = {
val diff = StringUtils.difference(text, other.text)
new Fragment(diff)
}
def ::(other: Fragment): Fragment = {
new Fragment(text + " || " + other.text)
}
}
![Page 10: Operator Overloading In Scala](https://reader033.vdocuments.net/reader033/viewer/2022051313/547ec1a2b4af9f9b158b57b1/html5/thumbnails/10.jpg)
Testing Interesting Names
package com.joeygibson.oopres
@RunWith(classOf[JUnitRunner])
class FragmentTest extends FlatSpec with ShouldMatchers {
it should "return proper differences" in {
val f0 = new Fragment("Scala is groovy")
val f1 = new Fragment("Scala is cool")
val diff = f0 Δ f1
diff.toString should equal("cool")
}
it should "concatenate properly" in {
val f0 = new Fragment("First Fragment")
val f1 = new Fragment("Second Fragment")
val f2 = f0 :: f1
f2.toString should equal ("Second Fragment || First Fragment")
}
}
![Page 11: Operator Overloading In Scala](https://reader033.vdocuments.net/reader033/viewer/2022051313/547ec1a2b4af9f9b158b57b1/html5/thumbnails/11.jpg)
You Can Even Get Silly…
package com.joeygibson.oopres
class Absurdity(val text: String) {
def \/\/(other: Absurdity) = {
new Absurdity(text + ", " + other.text)
}
def /\/\(other: Absurdity) = {
new Absurdity(other.text + ", " + text)
}
override def toString = text
}
![Page 12: Operator Overloading In Scala](https://reader033.vdocuments.net/reader033/viewer/2022051313/547ec1a2b4af9f9b158b57b1/html5/thumbnails/12.jpg)
Testing the Silliness
package com.joeygibson.oopres
@RunWith(classOf[JUnitRunner])
class AbsurdityTest extends FlatSpec with ShouldMatchers {
it should "do something absurd" in {
val a = new Absurdity("foo")
val b = new Absurdity("bar")
val c = a \/\/ b
c.toString should equal ("foo, bar")
}
it should "do something equally absurd" in {
val a = new Absurdity("foo")
val b = new Absurdity("bar")
val c = a /\/\ b
c.toString should equal ("bar, foo")
}
}
![Page 13: Operator Overloading In Scala](https://reader033.vdocuments.net/reader033/viewer/2022051313/547ec1a2b4af9f9b158b57b1/html5/thumbnails/13.jpg)
Arguments of Different Types
Define multiple versions of method, taking different
types
Use Implicit conversions
![Page 14: Operator Overloading In Scala](https://reader033.vdocuments.net/reader033/viewer/2022051313/547ec1a2b4af9f9b158b57b1/html5/thumbnails/14.jpg)
Multiple Defs of Operator
package com.joeygibson.oopres
class Complex2(val real: Int, val imaginary: Int) {
def +(operand: Complex2): Complex2 = {
new Complex2(real + operand.real, imaginary + operand.imaginary)
}
def +(operand: Int): Complex2 = this + new Complex2(operand, 0)
def *(operand: Complex2): Complex2 = {
new Complex2(real * operand.real - imaginary * operand.imaginary,
real * operand.imaginary + imaginary * operand.real)
}
def *(operand: Int): Complex2 = this * new Complex2(operand, 1)
def unary_- = new Complex2(-real, imaginary)
override def toString() = {
real + (if (imaginary < 0) "" else "+") + imaginary + "i"
}
}
![Page 15: Operator Overloading In Scala](https://reader033.vdocuments.net/reader033/viewer/2022051313/547ec1a2b4af9f9b158b57b1/html5/thumbnails/15.jpg)
Testing Multiple Defs
package com.joeygibson.oopres
@RunWith(classOf[JUnitRunner])
class Complex2Test extends FlatSpec with ShouldMatchers {
it should "convert int to Complex2" in {
val c = new Complex2(1, 2)
val d = c + 23
d.toString should equal ("24+2i")
}
// it should "convert int to Complex2, reversed" in {
// val c = new Complex2(1, 2)
// val d: Complex2 = 23 + c
//
// d.toString should equal ("24+2i")
// }
}
![Page 16: Operator Overloading In Scala](https://reader033.vdocuments.net/reader033/viewer/2022051313/547ec1a2b4af9f9b158b57b1/html5/thumbnails/16.jpg)
Implicit Conversions
The implicit function must be in scope
It can live in the companion object of either class
under consideration
Be careful with implicits, especially when using
common types
![Page 17: Operator Overloading In Scala](https://reader033.vdocuments.net/reader033/viewer/2022051313/547ec1a2b4af9f9b158b57b1/html5/thumbnails/17.jpg)
Implicit Conversions
package com.joeygibson.oopres
object Complex3 {
implicit def intToComplex3(anInt: Int): Complex3 = new Complex3(anInt, 0)
}
class Complex3(val real: Int, val imaginary: Int) {
def +(operand: Complex3): Complex3 = {
new Complex3(real + operand.real, imaginary + operand.imaginary)
}
def *(operand: Complex3): Complex3 = {
new Complex3(real * operand.real - imaginary * operand.imaginary,
real * operand.imaginary + imaginary * operand.real)
}
def unary_- = new Complex3(-real, imaginary)
override def toString() = {
real + (if (imaginary < 0) "" else "+") + imaginary + "i"
}
}
![Page 18: Operator Overloading In Scala](https://reader033.vdocuments.net/reader033/viewer/2022051313/547ec1a2b4af9f9b158b57b1/html5/thumbnails/18.jpg)
Testing Implicits
package com.joeygibson.oopres
@RunWith(classOf[JUnitRunner])
class Complex3Test extends FlatSpec with ShouldMatchers {
it should "convert int to Complex3" in {
val c = new Complex3(1, 2)
val d = c + 23
d.toString should equal ("24+2i")
}
it should "convert int to Complex3, reversed" in {
import com.joeygibson.oopres.Complex3.intToComplex3
val c = new Complex3(1, 2)
val d: Complex3 = 23 + c
d.toString should equal ("24+2i")
}
}
![Page 19: Operator Overloading In Scala](https://reader033.vdocuments.net/reader033/viewer/2022051313/547ec1a2b4af9f9b158b57b1/html5/thumbnails/19.jpg)
Using Both
package com.joeygibson.oopres
object Complex4 {
implicit def intToComplex4(anInt: Int): Complex4 = {
printf("Implicitly converting to Complex4\n")
new Complex4(anInt, 0)
}
}
class Complex4(val real: Int, val imaginary: Int) {
def +(operand: Complex4): Complex4 = {
new Complex4(real + operand.real, imaginary + operand.imaginary)
}
def +(operand: Int): Complex4 = this + new Complex4(operand, 0)
def *(operand: Complex4): Complex4 = {
new Complex4(real * operand.real - imaginary * operand.imaginary,
real * operand.imaginary + imaginary * operand.real)
}
def *(operand: Int): Complex4 = this * new Complex4(operand, 0)
def unary_- = new Complex4(-real, imaginary)
override def toString() = {
real + (if (imaginary < 0) "" else "+") + imaginary + "i"
}
}
![Page 20: Operator Overloading In Scala](https://reader033.vdocuments.net/reader033/viewer/2022051313/547ec1a2b4af9f9b158b57b1/html5/thumbnails/20.jpg)
You Can’t Use ++ as Prefix
package com.joeygibson.oopres
class MyNumber(private var num: Int) {
def number: Int = num
override def toString = num.toString
def ++ = {
val n = num
num += 1
new MyNumber(n)
}
def unary_++ = {
num += 1
this
}
}
![Page 21: Operator Overloading In Scala](https://reader033.vdocuments.net/reader033/viewer/2022051313/547ec1a2b4af9f9b158b57b1/html5/thumbnails/21.jpg)
The First One Works, The Second…
package com.joeygibson.oopres
@RunWith(classOf[JUnitRunner])
class MyNumberTest extends FlatSpec with ShouldMatchers {
it should "post increment" in {
val x = new MyNumber(23)
val z = x++
z.number should equal (23)
x.number should equal (24)
}
// it should "pre increment" in {
// val x = new MyNumber(23)
// val z = ++x
//
// z.number should equal (24)
// x.number should equal (24)
// }
}
![Page 22: Operator Overloading In Scala](https://reader033.vdocuments.net/reader033/viewer/2022051313/547ec1a2b4af9f9b158b57b1/html5/thumbnails/22.jpg)
ἐγώ ἐιμι
Email: [email protected]
Blog: http://joeygibson.com
Original post: http://bit.ly/wNvAl
Twitter: @joeygibson
Facebook: http://facebook.com/joeygibson