OPERATOR OVERLOADING
IN SCALA
Joey Gibson
First Things First
   Working with Java since 1996
   Playing with Scala for not-quite-a-year

   Originally a blog pos...
What Is Operator Overloading?
   A language facility that allows developers to define
    behavior for symbols like +, -,...
The Basics
   Technically, Scala does not have operator
    overloading
     Operatorsare just methods
     Any method ...
What About Precedence?
   Scala looks at the first character of the operator
    name
       All other special chars
   ...
Operator Naming
   Typically, you would use arithmetic operator
    symbols, but you don’t have to
   Since operators ar...
Complex.scala
package com.joeygibson.oopres

class Complex(val real: Int, val imaginary: Int) {
  def +(operand: Complex):...
ComplexTest.scala
package com.joeygibson.oopres


import org.junit.runner.RunWith
import org.scalatest.FlatSpec
import org...
Interesting Naming Examples
package com.joeygibson.oopres

import org.apache.commons.lang.StringUtils

class Fragment(val ...
Testing Interesting Names
package com.joeygibson.oopres


@RunWith(classOf[JUnitRunner])
class FragmentTest extends FlatSp...
You Can Even Get Silly…
package com.joeygibson.oopres

class Absurdity(val text: String) {
  def //(other: Absurdity) = {
...
Testing the Silliness
package com.joeygibson.oopres


@RunWith(classOf[JUnitRunner])
class AbsurdityTest extends FlatSpec ...
Arguments of Different Types
   Define multiple versions of method, taking different
    types
   Use Implicit conversio...
Multiple Defs of Operator
package com.joeygibson.oopres


class Complex2(val real: Int, val imaginary: Int) {
    def +(op...
Testing Multiple Defs
package com.joeygibson.oopres

@RunWith(classOf[JUnitRunner])
class Complex2Test extends FlatSpec wi...
Implicit Conversions
   The implicit function must be in scope
   It can live in the companion object of either class
  ...
Implicit Conversions
package com.joeygibson.oopres


object Complex3 {
    implicit def intToComplex3(anInt: Int): Complex...
Testing Implicits
package com.joeygibson.oopres

@RunWith(classOf[JUnitRunner])
class Complex3Test extends FlatSpec with S...
Using Both
package com.joeygibson.oopres



object Complex4 {

    implicit def intToComplex4(anInt: Int): Complex4 = {

 ...
You Can’t Use ++ as Prefix
package com.joeygibson.oopres


class MyNumber(private var num: Int) {


    def number: Int = ...
The First One Works, The Second…
package com.joeygibson.oopres


@RunWith(classOf[JUnitRunner])
class MyNumberTest extends...
ἐγώ ἐιμι
   Email: joey@joeygibson.com
   Blog: http://joeygibson.com
     Original   post: http://bit.ly/wNvAl
   Twi...
Upcoming SlideShare
Loading in …5
×

Operator Overloading In Scala

10,843 views

Published on

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.

Published in: Technology
  • Be the first to comment

Operator Overloading In Scala

  1. 1. OPERATOR OVERLOADING IN SCALA Joey Gibson
  2. 2. 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
  3. 3. 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
  4. 4. The Basics  Technically, Scala does not have operator overloading  Operatorsare 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_-
  5. 5. What About Precedence?  Scala looks at the first character of the operator name  All other special chars  */%  +-  :  =!  <>  &  ^  |  All letters  All assignment operators
  6. 6. 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
  7. 7. 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" } }
  8. 8. 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") } }
  9. 9. 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) } }
  10. 10. 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") } }
  11. 11. 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 }
  12. 12. 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") } }
  13. 13. Arguments of Different Types  Define multiple versions of method, taking different types  Use Implicit conversions
  14. 14. 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" } }
  15. 15. 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") // } }
  16. 16. 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
  17. 17. 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" } }
  18. 18. 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") } }
  19. 19. Using Both package com.joeygibson.oopres object Complex4 { implicit def intToComplex4(anInt: Int): Complex4 = { printf("Implicitly converting to Complex4n") 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" } }
  20. 20. 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 } }
  21. 21. 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) // } }
  22. 22. ἐγώ ἐιμι  Email: joey@joeygibson.com  Blog: http://joeygibson.com  Original post: http://bit.ly/wNvAl  Twitter: @joeygibson  Facebook: http://facebook.com/joeygibson

×