Skip to content

Latest commit

 

History

History
109 lines (83 loc) · 2.84 KB

lesson1_4_nested_conversions.md

File metadata and controls

109 lines (83 loc) · 2.84 KB

Nested Implicit Conversions

We have seen implicit conversions from one type to another:

import scala.language.implicitConversions

case class Dog() {
  def bark: String = "woof!"
}

case class Cat() {
  def meow: String = "meow"
}

case class Sheep() {
  def baa: String = "baa!"
}

implicit def dogToCat(dog: Dog): Cat = Cat()
implicit def catToSheep(cat: Cat): Sheep = Sheep()
@ Dog().meow
res0: String = "meow"

@ Cat().baa
res1: String = "baa!"

However, just because we have one conversion from Dog to Cat and another from Cat to Sheep does not mean that we can convert an instance of Dog directly to an instance of Sheep (in other words, dogs won't be able to baa):

@ Dog().baa
<console>:17: error: value baa is not a member of Dog
       Dog().baa
             ^

We can get around this by supplying an implicit parameter to the conversion methods:

implicit def dogToCat[D](dog: D)(implicit f: D => Dog): Cat = Cat()
implicit def catToSheep[C](cat: C)(implicit f: C => Cat): Sheep = Sheep()
@ Dog().baa
res3: String = "baa!"

Here the compiler will first search the implicit scope to see if there is an implicit conversion from Dog to another type that has the method baa (ie. in this case, that type would be Sheep). Such a conversion doesn't exist. HOWEVER, there is an implicit conversion between the generic type C and type Cat so the compiler will explore if it can use it somehow:

catToSheep(Dog())(...)

Next, the compiler will search the implicit scope for the implicit function C => Cat. Here we said that C is our type Dog and so it finds that the function dogToCat meets these requirements:

catToSheep(Dog())(dogToCat(_)(...))

You might be wondering what implicit function D => Dog is used for dogToCat. Since D is our type Dog, it means that the implicit function that we need will look like Dog => Dog. This is also known as the identity function which is automatically provided by the Scala standard library so the compiler uses that:

catToSheep(Dog())(dogToCat(_)(identity))

This approach of doing nested implicit conversions also works for implicit classes:

case class Dog() {
  def dog: String = "bark!"
}

implicit class Cat[D](dog: D)(implicit f: D => Dog) {
  def meow: String = "meow"
}

implicit class Sheep[D, C](cat: C)(implicit f: C => Cat[D]) {
  def baa: String = "baa!"
}
@ Dog().baa
res0: String = "baa!"