Code Examples for

Programming in Scala, Fourth Edition

Return to chapter index

21 Implicit Conversions and Parameters

Sample run of chapter's interpreter examples

21.1 Implicit conversions


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

21.2 Rules for implicits


// In file implicit-conversions-and-parameters/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>:7: 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 implicit-conversions-and-parameters/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 implicit-conversions-and-parameters/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) ... }
case class Rectangle(width: Int, height: Int)
implicit class RectangleMaker(width: Int) { def x(height: Int) = Rectangle(width, height) }
// Automatically generated implicit def RectangleMaker(width: Int) = new RectangleMaker(width)
scala> val myRectangle = 3 x 4 myRectangle: Rectangle = Rectangle(3,4)

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@714d36d6 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>:13: 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>
scala> Greeter.greet("Joe") <console>:19: 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>
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") }
// In file implicit-conversions-and-parameters/Misc.scala def maxListOrdering[T](elements: List[T]) (ordering: Ordering[T]): T = elements match { case List() => throw new IllegalArgumentException("empty list!") case List(x) => x case x :: rest => val maxRest = maxListOrdering(rest)(ordering) if (ordering.gt(x, maxRest)) x else maxRest }
// In file implicit-conversions-and-parameters/MaxList1.scala def maxListImpParm[T](elements: List[T]) (implicit ordering: Ordering[T]): T = elements match { case List() => throw new IllegalArgumentException("empty list!") case List(x) => x case x :: rest => val maxRest = maxListImpParm(rest)(ordering) if (ordering.gt(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: String = two
def maxListPoorStyle[T](elements: List[T]) (implicit orderer: (T, T) => Boolean): T

21.6 Context bounds


// In file implicit-conversions-and-parameters/Misc.scala def maxList[T](elements: List[T]) (implicit ordering: Ordering[T]): T = elements match { case List() => throw new IllegalArgumentException("empty list!") case List(x) => x case x :: rest => val maxRest = maxList(rest) // (ordering) is implicit if (ordering.gt(x, maxRest)) x // this ordering is else maxRest // still explicit }
def implicitly[T](implicit t: T) = t
// In file implicit-conversions-and-parameters/Misc.scala def maxList[T](elements: List[T]) (implicit comparator: Ordering[T]): T = // same body...
// In file implicit-conversions-and-parameters/Misc.scala def maxList[T](elements: List[T]) (implicit ordering: Ordering[T]): T = elements match { case List() => throw new IllegalArgumentException("empty list!") case List(x) => x case x :: rest => val maxRest = maxList(rest) if (implicitly[Ordering[T]].gt(x, maxRest)) x else maxRest }
// In file implicit-conversions-and-parameters/Misc.scala def maxList[T](elements: List[T]) (implicit iceCream: Ordering[T]): T = // same body...
// In file implicit-conversions-and-parameters/Misc.scala def maxList[T : Ordering](elements: List[T]): T = elements match { case List() => throw new IllegalArgumentException("empty list!") case List(x) => x case x :: rest => val maxRest = maxList(rest) if (implicitly[Ordering[T]].gt(x, maxRest)) x else maxRest }

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 scala> implicit def intToDigits(i: Int) = | i.toString.toList.map(_.toInt) intToDigits: (i: Int)List[Int] scala> printLength(12) <console>:26: error: type mismatch; found : Int(12) required: Seq[Int] Note that implicit conversions are not applicable because they are ambiguous: both method intToRange of type (i: Int)scala.collection.immutable.Range.Inclusive and method intToDigits of type (i: Int)List[Int] are possible conversion functions from Int(12) to Seq[Int] printLength(12) ^
val cba = "abc".reverse

21.8 Debugging implicits


scala> val chars: List[Char] = "xyz" <console>:24: error: type mismatch; found : String("xyz") required: List[Char] val chars: List[Char] = "xyz" ^
scala> val chars: List[Char] = wrapString("xyz") <console>:24: error: type mismatch; found : scala.collection.immutable.WrappedString required: List[Char] val chars: List[Char] = wrapString("xyz") ^
Mocha.this.enjoy("reader")(Mocha.this.pref)
// In file implicit-conversions-and-parameters/Mocha.scala object Mocha extends App { 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.9 Conclusion

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

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

and:

http://booksites.artima.com/programming_in_scala_4ed

Copyright © 2007-2019 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.