Code Examples for

Programming in Scala, Second Edition

Return to chapter index

21 Implicit Conversions and Parameters

Sample run of chapter's interpreter examples

21.1 Implicit conversions


// In file implicits/Swing1.scala val button = new JButton button.addActionListener( new ActionListener { def actionPerformed(event: ActionEvent) { println("pressed!") } } )
// In file implicits/Swing2.scala button.addActionListener( // Type mismatch! (_: ActionEvent) => println("pressed!") )
// In file implicits/Swing2.scala implicit def function2ActionListener(f: ActionEvent => Unit) = new ActionListener { def actionPerformed(event: ActionEvent) = f(event) }
button.addActionListener( function2ActionListener( (_: ActionEvent) => println("pressed!") ) )
// Now this works button.addActionListener( (_: ActionEvent) => println("pressed!") )

21.2 Rules for implicits


// In file implicits/Misc.scala implicit def intToString(x: Int) = x.toString
object Dollar { implicit def dollarToEuro(x: Dollar): Euro = ... } class Dollar { ... }
object MyConversions { implicit def stringWrapper(s: String): IndexedSeq[Char] = ... implicit def intToString(x: Int): String = ... }
import MyConversions.stringWrapper ... // code making use of stringWrapper

21.3 Implicit conversion to an expected type


scala> val i: Int = 3.5 <console>:4: error: type mismatch; found : Double(3.5) required: Int val i: Int = 3.5 ^
scala> implicit def doubleToInt(x: Double) = x.toInt doubleToInt: (x: Double)Int scala> val i: Int = 3.5 i: Int = 3
val i: Int = doubleToInt(3.5)
// In file implicits/Misc.scala implicit def int2double(x: Int): Double = x.toDouble

21.4 Converting the receiver


class Rational(n: Int, d: Int) { ... def + (that: Rational): Rational = ... def + (that: Int): Rational = ... }
scala> val oneHalf = new Rational(1, 2) oneHalf: Rational = 1/2 scala> oneHalf + oneHalf res0: Rational = 1/1 scala> oneHalf + 1 res1: Rational = 3/2
scala> 1 + oneHalf <console>:6: error: overloaded method value + with alternatives (Double)Double <and> ... cannot be applied to (Rational) 1 + oneHalf ^
scala> implicit def intToRational(x: Int) = | new Rational(x, 1) intToRational: (x: Int)Rational
scala> 1 + oneHalf res2: Rational = 3/2
intToRational(1) + oneHalf
// In file implicits/Misc.scala Map(1 -> "one", 2 -> "two", 3 -> "three")
package scala object Predef { class ArrowAssoc[A](x: A) { def -> [B](y: B): Tuple2[A, B] = Tuple2(x, y) } implicit def any2ArrowAssoc[A](x: A): ArrowAssoc[A] = new ArrowAssoc(x) ... }

21.5 Implicit parameters


class PreferredPrompt(val preference: String)
object Greeter { def greet(name: String)(implicit prompt: PreferredPrompt) { println("Welcome, "+ name +". The system is ready.") println(prompt.preference) } }
scala> val bobsPrompt = new PreferredPrompt("relax> ") bobsPrompt: PreferredPrompt = PreferredPrompt@74a138 scala> Greeter.greet("Bob")(bobsPrompt) Welcome, Bob. The system is ready. relax>
object JoesPrefs { implicit val prompt = new PreferredPrompt("Yes, master> ") }
scala> Greeter.greet("Joe") <console>:10: error: could not find implicit value for parameter prompt: PreferredPrompt Greeter.greet("Joe") ^
scala> import JoesPrefs._ import JoesPrefs._ scala> Greeter.greet("Joe") Welcome, Joe. The system is ready. Yes, master>
class PreferredPrompt(val preference: String) class PreferredDrink(val preference: String) object Greeter { def greet(name: String)(implicit prompt: PreferredPrompt, drink: PreferredDrink) { println("Welcome, "+ name +". The system is ready.") print("But while you work, ") println("why not enjoy a cup of "+ drink.preference +"?") println(prompt.preference) } } object JoesPrefs { implicit val prompt = new PreferredPrompt("Yes, master> ") implicit val drink = new PreferredDrink("tea") }
scala> Greeter.greet("Joe") <console>:14: error: could not find implicit value for parameter prompt: PreferredPrompt Greeter.greet("Joe") ^
scala> import JoesPrefs._ import JoesPrefs._
scala> Greeter.greet("Joe")(prompt, drink) Welcome, Joe. The system is ready. But while you work, why not enjoy a cup of tea? Yes, master>
scala> Greeter.greet("Joe") Welcome, Joe. The system is ready. But while you work, why not enjoy a cup of tea? Yes, master>
// In file implicits/Misc.scala def maxListUpBound[T <: Ordered[T]](elements: List[T]): T = elements match { case List() => throw new IllegalArgumentException("empty list!") case List(x) => x case x :: rest => val maxRest = maxListUpBound(rest) if (x > maxRest) x else maxRest }
// In file implicits/MaxList1.scala def maxListImpParm[T](elements: List[T]) (implicit orderer: T => Ordered[T]): T = elements match { case List() => throw new IllegalArgumentException("empty list!") case List(x) => x case x :: rest => val maxRest = maxListImpParm(rest)(orderer) if (orderer(x) > maxRest) x else maxRest }
scala> maxListImpParm(List(1,5,10,3)) res9: Int = 10 scala> maxListImpParm(List(1.5, 5.2, 10.7, 3.14159)) res10: Double = 10.7 scala> maxListImpParm(List("one", "two", "three")) res11: java.lang.String = two
def maxListPoorStyle[T](elements: List[T]) (implicit orderer: (T, T) => Boolean): T

21.6 View bounds


// In file implicits/Misc.scala def maxList[T](elements: List[T]) (implicit orderer: T => Ordered[T]): T = elements match { case List() => throw new IllegalArgumentException("empty list!") case List(x) => x case x :: rest => val maxRest = maxList(rest) // (orderer) is implicit if (x > maxRest) x // orderer(x) is implicit else maxRest }
// In file implicits/Misc.scala def maxList[T](elements: List[T]) (implicit converter: T => Ordered[T]): T = // same body...
// In file implicits/Misc.scala def maxList[T](elements: List[T]) (implicit iceCream: T => Ordered[T]): T = // same body...
// In file implicits/MaxList2.scala def maxList[T <% Ordered[T]](elements: List[T]): T = elements match { case List() => throw new IllegalArgumentException("empty list!") case List(x) => x case x :: rest => val maxRest = maxList(rest) // (orderer) is implicit if (x > maxRest) x // orderer(x) is implicit else maxRest }
implicit def identity[A](x: A): A = x

21.7 When multiple conversions apply


scala> def printLength(seq: Seq[Int]) = println(seq.length) printLength: (seq: Seq[Int])Unit scala> implicit def intToRange(i: Int) = 1 to i intToRange: (i: Int)scala.collection.immutable.Range.Inclusive with scala.collection.immutable.Range.ByOne scala> implicit def intToDigits(i: Int) = | i.toString.toList.map(_.toInt) intToDigits: (i: Int)List[Int] scala> printLength(12) <console>:21: error: type mismatch; found : Int(12) required: Seq[Int] Note that implicit conversions are not applicable because they are ambiguous: ...
val cba = "abc".reverse
// In file implicits/mocha.scala object Mocha extends Application { class PreferredDrink(val preference: String) implicit val pref = new PreferredDrink("mocha") def enjoy(name: String)(implicit drink: PreferredDrink) { print("Welcome, "+ name) print(". Enjoy a ") print(drink.preference) println("!") } enjoy("reader") }
$ scalac -Xprint:typer mocha.scala [[syntax trees at end of typer]]// Scala source: mocha.scala package <empty> { final object Mocha extends java.lang.Object with Application with ScalaObject { // ... private[this] val pref: Mocha.PreferredDrink = new Mocha.this.PreferredDrink("mocha"); implicit <stable> <accessor> def pref: Mocha.PreferredDrink = Mocha.this.pref; def enjoy(name: String) (implicit drink: Mocha.PreferredDrink): Unit = { scala.this.Predef.print("Welcome, ".+(name)); scala.this.Predef.print(". Enjoy a "); scala.this.Predef.print(drink.preference); scala.this.Predef.println("!") }; Mocha.this.enjoy("reader")(Mocha.this.pref) } }

21.8 Debugging implicits


scala> val chars: List[Char] = "xyz" <console>:19: error: type mismatch; found : java.lang.String("xyz") required: List[Char] val chars: List[Char] = "xyz" ^
scala> val chars: List[Char] = wrapString("xyz") <console>:19: error: type mismatch; found : scala.collection.immutable.WrappedString required: List[Char] val chars: List[Char] = wrapString("xyz") ^
Mocha.this.enjoy("reader")(Mocha.this.pref)

21.9 Conclusion

For more information about Programming in Scala, Second Edition (the "Stairway Book"), please visit:

http://www.artima.com/shop/programming_in_scala_2ed

and:

http://booksites.artima.com/programming_in_scala_2ed

Copyright © 2007-2010 Artima, Inc. All rights reserved.

Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the License. You may obtain a copy of the License at

http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the specific language governing permissions and limitations under the License.