Slide 1

Slide 1 text

Demystifying implicits Luminita Apostol

Slide 2

Slide 2 text

Agenda Intro to implicits Implicits resolution algorithm Conclusions

Slide 3

Slide 3 text

Intro to implicits Common use Implicit parameters Implicit values and functions Implicit conversions Use of implicit conversions Implicit classes Context bounds

Slide 4

Slide 4 text

Common use mark a parameter and a value with implicit def example(implicit x: Int) = println(x) implicit val element: Int = 5 example

Slide 5

Slide 5 text

Common use mark a parameter and a value with implicit def example(implicit x: Int) = println(x) implicit val element: Int = 5 example // 5

Slide 6

Slide 6 text

Implicit parameters tells the compiler that the argument list might be missing compiler should resolve the arguments via implicit resolution def example1(implicit x: Int = 5) def example2(implicit x: Int, y: Int) def example3(x: Int, implicit y: Int) def example4(x: Int)(implicit y: Int) def example5(implicit x: Int)(y: Int) def example6(implicit x: Int)(implicit y: Int)

Slide 7

Slide 7 text

Implicit parameters tells the compiler that the argument list might be missing compiler should resolve the arguments via implicit resolution Note: implicit parameters can have default values all parameters following it will be implicit must be the last parameter list given methods can have only one implicit parameter list def example1(implicit x: Int = 5) // correct def example2(implicit x: Int, y: Int) // correct def example3(x: Int, implicit y: Int) // compilation error def example4(x: Int)(implicit y: Int) // correct def example5(implicit x: Int)(y: Int) // compilation error def example6(implicit x: Int)(implicit y: Int) // compilation error

Slide 8

Slide 8 text

Implicit values and functions tells the compiler that those definitions can be used during implicit resolution implicit val b1: Bar = new Bar { override def toString = "val Bar"} implicit def b2(): Bar = new Bar { override def toString = "def Bar"

Slide 9

Slide 9 text

Implicit conversions (views) an implicit value of unary function type A => B an implicit method that has in its first parameter section a single, non-implicit parameter implicit val conv: String => Int = (s: String) => s.length implicit def stringToInt(s: String): Int = s.length implicit def listToX(xs: List[T])(implicit f: T => X):

Slide 10

Slide 10 text

Use of implicit conversions an expression doesn't meet the type expected by the compiler def foo(msg: String) = println(msg) foo(5) implicit def intToString(x: Int): String = x.toString

Slide 11

Slide 11 text

Use of implicit conversions an expression doesn't meet the type expected by the compiler a selection e.t doesn't have a t member def foo(msg: String) = println(msg) foo(5) // 5 implicit def intToString(x: Int): String = x.toString "foo".foo() implicit def stringToFoo(x: String): Foo = new Foo { def foo() = println(s"String $x foos") } trait Foo { def foo(): Unit }

Slide 12

Slide 12 text

Use of implicit conversions an expression doesn't meet the type expected by the compiler a selection e.t doesn't have a t member def foo(msg: String) = println(msg) foo(5) // 5 implicit def intToString(x: Int): String = x.toString "foo".foo() // String foo foos implicit def stringToFoo(x: String): Foo = new Foo { def foo() = println(s"String $x foos") } trait Foo { def foo(): Unit }

Slide 13

Slide 13 text

Implicit classes makes the class’ primary constructor available for implicit conversions implicit class RichInt(a: Int) { def foo() = println(s"Int $a foos") } 3.foo()

Slide 14

Slide 14 text

Implicit classes makes the class’ primary constructor available for implicit conversions Note: they must be defined inside of another trait/class/object they may only take one non-implicit argument in their constructor there may not be any method, member or object in scope with the same name as the implicit class implicit class RichInt(a: Int) { def foo() = println(s"Int $a foos") } 3.foo() // Int 3 foos

Slide 15

Slide 15 text

Context bounds declares that there must be an implicit value available with a given type def foo[A](x: A)(implicit ev: B[A]) // equivalent to def foo[A : B](x: A)

Slide 16

Slide 16 text

Implicit resolution algorithm 1. First looks in the 2. Next looks in the - all companion modules of classes associated with implicit parameter's type current scope implicit scope

Slide 17

Slide 17 text

Current scope IDENTIFIERS: A DIGRESSION identifiers play a crucial role in the selection of implicits how the compiler resolves identifiers within a particular scope

Slide 18

Slide 18 text

Scala definitions entity = types, values, methods, or classes binding = reference to entities using identifiers or names scope = lexical boundary in which bindings are available

Slide 19

Slide 19 text

Bindings shadowing Scope can be nested - when creating a new scope, bindings from the outer scope are still available class Foo(x : Int) { def tmp = { val x = 2 x } } bindings of higher precedence shadow bindings of lower precedence within the same scope bindings of higher or the same precedence shadow bindings in an outer scope Note: shadowing is only a partial order

Slide 20

Slide 20 text

Teaser ? // ExternalBinding.scala package play.bindings object x { override def toString = "4" } object Wildcard { def x = "3" } object Explicit { def x = "2" } // Main.scala package play.bindings object Main extends App { val x = "1" import Wildcard._ import Explicit.x println(x) }

Slide 21

Slide 21 text

Scala binding precedence 1. Definitions and declarations that are local, inherited, or made available by a package clause in the same source file where the definition occurs 2. Explicit imports 3. Wildcard imports 4. Definitions made available by a package clause not in the source file where the definition occurs

Slide 22

Slide 22 text

How it relates to implicit resolution It uses the same mechanism: 1. Implicits that are local, inherited or members of an enclosing template 2. Explicit imports 3. Wildcard imports 4. N/A

Slide 23

Slide 23 text

Implicit scope Defined by all companion modules of classes that are associated with the implicit parameter's type T Form of T Associated classes T1 extends T2 with T3 {T1, T2, T3} T1[S1, S2] {T1, S1, S2} T1 nested within S1 {T1, S1, associated classes(S1)} type T1 = S1 with S2 {S1, S2 & their associated classes} T1 => S1 view {T1, S1 & their associated classes}

Slide 24

Slide 24 text

Companion object List(1, 2, 3).sorted def sorted[B >: A](implicit ord: math.Ordering[B]): List[A] object Ordering { trait IntOrdering extends Ordering[Int] { def compare(x: Int, y: Int) = if (x < y) ­1 else if (x == y) 0 else 1 } implicit object Int extends IntOrdering }

Slide 25

Slide 25 text

Companion objects of the superclasses class A class B extends A object A { implicit def aToString(a: A): String = "A object" } val s: String = new B Note: the most specific argument that matches the implicit parameter's type will be chosen using the rules of static overloading resolution

Slide 26

Slide 26 text

Implicit conversion List(1, 2, 3).flatMap(x => Some(0).map(y => (x, y))) def flatMap[B, That](f: A => GenTraversableOnce[B]) object Option { /** An implicit conversion that converts an option to an iterable value * implicit def option2Iterable[A](xo: Option[A]): Iterable[A] = xo.toList }

Slide 27

Slide 27 text

Outer objects for nested types // for summoning implicit values from the nether world def implicitly[T](implicit e: T) = e object A { object B { override def toString = "B object" } implicit val b: B.type = B } println(implicitly[B.type])

Slide 28

Slide 28 text

Outer objects for nested types // for summoning implicit values from the nether world def implicitly[T](implicit e: T) = e object A { object B { override def toString = "B object" } implicit val b: B.type = B } println(implicitly[B.type]) // B object

Slide 29

Slide 29 text

Companion object of type arguments List(new Foo(5), new Foo(17), new Foo(4)).sorted class Foo(val n: Int) object Foo { implicit val ord: Ordering[Foo] = new Ordering[Foo] { def compare(x: Foo, y: Foo) = implicitly[Ordering[Int]].compare(x.n, } } def sorted[B >: A](implicit ord: Ordering[B])

Slide 30

Slide 30 text

Companion object of type arguments List(new Foo(5), new Foo(17), new Foo(4)).sorted // List(4, 5, 17) class Foo(val n: Int) object Foo { implicit val ord: Ordering[Foo] = new Ordering[Foo] { def compare(x: Foo, y: Foo) = implicitly[Ordering[Int]].compare(x.n, } } def sorted[B >: A](implicit ord: Ordering[B])

Slide 31

Slide 31 text

Package objects Any class that’s defined within a package is nested inside the package Any implicits defined on a package object will be on the implicit scope for all types defined inside the package Handy location to store implicits rather than defining companion objects for every type in a package

Slide 32

Slide 32 text

Why are implicits great useful for removing boiler plate parameter passing can make your code more readable* decoupled dependency injection class extensions type classes flexibility

Slide 33

Slide 33 text

No content

Slide 34

Slide 34 text

Pitfalls "If a programmer is not familiar with all the views in scope, the code is harder to interpret." "Implicit views are the most abused feature in Scala." "Implicits make Scala code look sexy yet impossible to understand. Sad but true, it is easy to write, but not to understand." "It is common to use package objects with implicits, so there is no way to eyeball that this small import on top of a file feeds several function calls with implicit variables." Checkout on Implicit Kryptonite http://scalapuzzlers.com

Slide 35

Slide 35 text

With great power comes great responsibility limit the number of implicits that are in scope provide implicits that can be overridden or hidden limit importable implicits package objects singleton objects that have the postfix Implicits implicits without the import tax

Slide 36

Slide 36 text

References Scala in depth Scala lang - Implicits Implicit Parameters in Scala Scala docs - Finding implicits Joshua Suereth - Implicits without the import tax Sohum Banerjea - Demystifying implicits and typeclasses in scala Spiridon - Scala implicit hell Programming in Scala - Implicit Conversions and Parameters

Slide 37

Slide 37 text

No content

Slide 38

Slide 38 text

Thank you! Luminita Apostol