Your SlideShare is downloading. ×
  • Like
Scala Implicits - Not to be feared
Upcoming SlideShare
Loading in...5
×

Thanks for flagging this SlideShare!

Oops! An error has occurred.

×

Now you can save presentations on your phone or tablet

Available for both IPhone and Android

Text the download link to your phone

Standard text messaging rates apply

Scala Implicits - Not to be feared

  • 10,367 views
Published

These are the slides from a talk I gave at the Waterloo Scala Meetup on October 9th 2013. The talk was geared toward describing the purpose of implicits, use cases, and getting past that initial hump …

These are the slides from a talk I gave at the Waterloo Scala Meetup on October 9th 2013. The talk was geared toward describing the purpose of implicits, use cases, and getting past that initial hump of "what are they and why would I need them" in order to get people to start exploring the ideas.

Published in Education , Technology , Business
  • Full Name Full Name Comment goes here.
    Are you sure you want to
    Your message goes here
  • @indykish Oh it took a while :) The design was new to me, so I spent some time screwing around with the colours, fonts, etc... Then I went back to it about 5 times to delete some content, clean it up, simplify it and so forth.

    I'd say it was about 6-8 hours worth of work over 5 or 6 days. I'm hoping the next one will be quicker, now that I've learned better how to manipulate the app
    Are you sure you want to
    Your message goes here
  • How long did it take to create this ? The visual design is super awesome. The way you narrate is splendid.
    Are you sure you want to
    Your message goes here
  • Dear Derek, How long did it take to prepare the presentation ? The visual appeal, and the quality is super awesome. The way you narrate stuff is really spendid.
    Are you sure you want to
    Your message goes here
  • @retronym Holy crap, you're right. I'm not sure where I read that an implicit class automatically extends AnyVal, but I was damn sure it was true. Now I have to feel guilty about spreading lies.

    Thanks.
    Are you sure you want to
    Your message goes here
  • Another rule of thumb: annotate the return types of implicits. Otherwise they might be discarded in implicit searches higher up in the same file that defines them.
    Are you sure you want to
    Your message goes here
No Downloads

Views

Total Views
10,367
On SlideShare
0
From Embeds
0
Number of Embeds
16

Actions

Shares
Downloads
172
Comments
7
Likes
30

Embeds 0

No embeds

Report content

Flagged as inappropriate Flag as inappropriate
Flag as inappropriate

Select your reason for flagging this presentation as inappropriate.

Cancel
    No notes for slide

Transcript

  • 1. Implicits Scalain Derek Wyatt Twitter: @derekwyatt Email: derek@derekwyatt.org Friday, 11 October, 13
  • 2. Agenda Lies and Damn Lies Use Cases Scope ExampleCode! Truths Rules of Thumb Friday, 11 October, 13
  • 3. Implicits are NEW Friday, 11 October, 13
  • 4. Implicits are NEW New things can be cool! Friday, 11 October, 13
  • 5. Implicits are NEW New things can be cool! New things can also be scary! ❉ Scala 2.10’s new set of warnings don’t help the situation. ❉ Friday, 11 October, 13
  • 6. Implicits are NEW New things can be cool! New things can also be scary! ❉ Scala 2.10’s new set of warnings don’t help the situation. ❉ Especially when they’re complicated! Friday, 11 October, 13
  • 7. Implicits are NEW New things can be cool! Newness + Complexness = Fearsometimes New things can also be scary! ❉ Scala 2.10’s new set of warnings don’t help the situation. ❉ Especially when they’re complicated! Friday, 11 October, 13
  • 8. Implicits are NEW New things can be cool! Newness + Complexness = Fearsometimes Fear Avoidance New things can also be scary! ❉ Scala 2.10’s new set of warnings don’t help the situation. ❉ Especially when they’re complicated! Friday, 11 October, 13
  • 9. Implicits are NEW New things can be cool! Newness + Complexness = Fearsometimes Fear Avoidance Avoidance :( New things can also be scary! ❉ Scala 2.10’s new set of warnings don’t help the situation. ❉ Especially when they’re complicated! Friday, 11 October, 13
  • 10. Implicits Friday, 11 October, 13
  • 11. Implicits Are Not Friday, 11 October, 13
  • 12. Implicits Are Not Global variables LIE! Friday, 11 October, 13
  • 13. Implicits Are Not Global variables Dynamically Applied LIE! DAMN LIE! Friday, 11 October, 13
  • 14. Implicits Are Not Global variables Dynamically Applied Dangerous (...much...) LIE! DAMN LIE! Fib Friday, 11 October, 13
  • 15. Implicits Are Not Global variables Dynamically Applied Dangerous (...much...) Implicits are like scissors. Use them. Don’t run with them. LIE! DAMN LIE! Fib Friday, 11 October, 13
  • 16. Use Cases What are they used for? Friday, 11 October, 13
  • 17. Use Case: Type Classes Friday, 11 October, 13
  • 18. Use Case: Type Classes val a = someInt() val b = someOtherInt() val lesser = if (lessThan(a, b)) a else b Friday, 11 October, 13
  • 19. Use Case: Type Classes val a = someInt() val b = someOtherInt() val lesser = if (lessThan(a, b)) a else b lessThan needsdefinition Friday, 11 October, 13
  • 20. Use Case: Type Classes def lessThan[A](a: A, b: A)(implicit o: Ordering[A]): Boolean = o.lt(a, b) val a = someInt() val b = someOtherInt() val lesser = if (lessThan(a, b)) a else b lessThan needsdefinition Friday, 11 October, 13
  • 21. Use Case: Type Classes def lessThan[A](a: A, b: A)(implicit o: Ordering[A]): Boolean = o.lt(a, b) val a = someInt() val b = someOtherInt() val lesser = if (lessThan(a, b)) a else b Ordering[Int]’s gottacome from somewhere lessThan needsdefinition Friday, 11 October, 13
  • 22. Use Case: Type Classes def lessThan[A](a: A, b: A)(implicit o: Ordering[A]): Boolean = o.lt(a, b) implicit object intOrdering extends Ordering[Int] { def compare(a: Int, b: Int): Int = a - b } val a = someInt() val b = someOtherInt() val lesser = if (lessThan(a, b)) a else b Ordering[Int]’s gottacome from somewhere lessThan needsdefinition Friday, 11 October, 13
  • 23. Use Case: Type Classes def lessThan[A](a: A, b: A)(implicit o: Ordering[A]): Boolean = o.lt(a, b) implicit object intOrdering extends Ordering[Int] { def compare(a: Int, b: Int): Int = a - b } val a = someInt() val b = someOtherInt() val lesser = if (lessThan(a, b)) a else b Ordering[Int]’s gottacome from somewhere val a = someInt() val b = someOtherInt() val lesser = if (lessThan(a, b)(intOrdering)) a else b lessThan needsdefinition Friday, 11 October, 13
  • 24. Use Case: Class Extension Friday, 11 October, 13
  • 25. Use Case: Class Extension val hexVals = “implicit”.asHexSeq // Vector(0x69, 0x6D, 0x70, 0x6C, 0x69, 0x63, 0x69, 0x74) The “Pimp” Friday, 11 October, 13
  • 26. Use Case: Class Extension val hexVals = “implicit”.asHexSeq // Vector(0x69, 0x6D, 0x70, 0x6C, 0x69, 0x63, 0x69, 0x74) The “Pimp” implicit class HexableString(s: String) { def asHexSeq: Seq[String] = s map { c => f”0x$c%02X” } } The implicit class definition provides the extension method Friday, 11 October, 13
  • 27. Use Case: Class Extension val hexVals = “implicit”.asHexSeq // Vector(0x69, 0x6D, 0x70, 0x6C, 0x69, 0x63, 0x69, 0x74) The “Pimp” implicit class HexableString(s: String) { def asHexSeq: Seq[String] = s map { c => f”0x$c%02X” } } The implicit class definition provides the extension method Bonus: If you extend implicit classes from AnyVal, no temporary object construction will occur. Friday, 11 October, 13
  • 28. Use Case: Decluttering val future1 = someCall(“a parameter”, 5.seconds) val future2 = someCall(345, 5.seconds) val future3 = someCall(235.9352, 5.seconds) Friday, 11 October, 13
  • 29. Use Case: Decluttering val future1 = someCall(“a parameter”, 5.seconds) val future2 = someCall(345, 5.seconds) val future3 = someCall(235.9352, 5.seconds) Blurgh Friday, 11 October, 13
  • 30. Use Case: Decluttering val future1 = someCall(“a parameter”, 5.seconds) val future2 = someCall(345, 5.seconds) val future3 = someCall(235.9352, 5.seconds) Blurgh def someCall[A](a: A)(implicit timeout: Duration): Future[A] = ??? But, if we define someCall this way... Friday, 11 October, 13
  • 31. Use Case: Decluttering val future1 = someCall(“a parameter”, 5.seconds) val future2 = someCall(345, 5.seconds) val future3 = someCall(235.9352, 5.seconds) Blurgh def someCall[A](a: A)(implicit timeout: Duration): Future[A] = ??? But, if we define someCall this way... implicit val myTimeoutValue = 5.seconds val future1 = someCall(“a parameter”) val future2 = someCall(345) val future3 = someCall(235.9352) And define an implicit value, we can simplify the calls... Friday, 11 October, 13
  • 32. Use Case: Internal DSLs Create your own sub-language with ease Friday, 11 October, 13
  • 33. Use Case: Internal DSLs Create your own sub-language with ease implicit class Recoverable[A](f: => A) { def recover(g: Throwable => A): A = try { f } catch { case t: Throwable => g(t) } } Friday, 11 October, 13
  • 34. Use Case: Internal DSLs Create your own sub-language with ease implicit class Recoverable[A](f: => A) { def recover(g: Throwable => A): A = try { f } catch { case t: Throwable => g(t) } } def thisThrows(): Int = throw new Exception(“Argh!”) val stable = thisThrows() recover { t => if (t.getMessage == “Argh!”) 10 else 5 } // stable == 10 Friday, 11 October, 13
  • 35. Use Case: Other stuff... Friday, 11 October, 13
  • 36. Use Case: Other stuff... Overriding defaults def func[A](a: A)(implicit tout: Duration = 3.seconds): Future[A] Friday, 11 October, 13
  • 37. Use Case: Other stuff... Overriding defaults def func[A](a: A)(implicit tout: Duration = 3.seconds): Future[A] Decoupled Dependency Injection class Database(ec: ExecutionContext) { def create(row: Row): Future[Result] = ??? def delete(id: RowId): Future[Result] = ??? // etc... } Friday, 11 October, 13
  • 38. Use Case: Other stuff... Overriding defaults def func[A](a: A)(implicit tout: Duration = 3.seconds): Future[A] Decoupled Dependency Injection class Database(ec: ExecutionContext) { def create(row: Row): Future[Result] = ??? def delete(id: RowId): Future[Result] = ??? // etc... } NO! Friday, 11 October, 13
  • 39. Use Case: Other stuff... Overriding defaults def func[A](a: A)(implicit tout: Duration = 3.seconds): Future[A] Decoupled Dependency Injection class Database { def create(row: Row)(implicit ec: ExecutionContext): Future[Result] def delete(id: RowId)(implicit ec: ExecutionContext): Future[Result] // etc... } We can now vary the ExecutionContext at any point by supplying the right implicit value Friday, 11 October, 13
  • 40. Implicit Scope Rules!!!! Friday, 11 October, 13
  • 41. Implicit Scope Rules!!!! There are a Lot of Rules Friday, 11 October, 13
  • 42. Implicit Scope Rules!!!! There are a Lot of Rules Read “Scala In Depth”* *Josh Suereth Friday, 11 October, 13
  • 43. Implicit Scope Rules!!!! There are a Lot of Rules Read “Scala In Depth”* Implicits without the Import Tax* *Josh Suereth Friday, 11 October, 13
  • 44. Implicit Scope Rules!!!! There are a Lot of Rules Read “Scala In Depth”* Implicits without the Import Tax* *Josh Suereth I don’t know them super well, and I haven’t cut my arm off yet... Friday, 11 October, 13
  • 45. Creating a Protocol Friday, 11 October, 13
  • 46. Creating a Protocol an Implicit Friday, 11 October, 13
  • 47. Creating a Protocol an Implicit We want: actor emit Message(“Hello”) Friday, 11 October, 13
  • 48. Creating a Protocol an Implicit We want: actor emit Message(“Hello”) To Produce: actor ! Envelope(ComponentType(“Client”), ComponentType(“DBActor”), ComponentId(“/user/supervisor/DB”), WorkId(“764efa883dd7671c4a3bbd9e”), MsgType(“org.my.Message”), MsgNum(1), Message(“Hello”)) Friday, 11 October, 13
  • 49. An Actor Derivation trait EnvelopingActor extends Actor with EnvelopeImplicits with ActorRefImplicits { implicit val myCompType = ComponentType(getClass.getSimpleName) implicit val myCompId = ComponentId(self.path) private var currentWorkId = unknownWorkId implicit def workId: WorkId = currentWorkId private var currentMsgNum = MsgNum(-1) implicit def msgNum: MsgNum = currentMsgNum def derivedReceive: Receive def derivedReceiveWrapper(wrapped: Receive): Receive = ??? final def receive = derivedReceiveWrapper(derivedReceive) } Friday, 11 October, 13
  • 50. An Actor Derivation trait EnvelopingActor extends Actor with EnvelopeImplicits with ActorRefImplicits { implicit val myCompType = ComponentType(getClass.getSimpleName) implicit val myCompId = ComponentId(self.path) private var currentWorkId = unknownWorkId implicit def workId: WorkId = currentWorkId private var currentMsgNum = MsgNum(-1) implicit def msgNum: MsgNum = currentMsgNum def derivedReceive: Receive def derivedReceiveWrapper(wrapped: Receive): Receive = ??? final def receive = derivedReceiveWrapper(derivedReceive) } Sets up the implicits in a high priority scope Friday, 11 October, 13
  • 51. The Receive Wrapper def derivedReceiveWrapper(wrapped: Receive): Receive = { case Envelope(_, _, _, workId, _, messageNum, message) => currentWorkIdVar = workId currentMessageNumVar = messageNum wrapped(message) case message => currentWorkIdVar = createWorkId() currentMessageNumVar = MessageNum(-1) wrapped(message) } Friday, 11 October, 13
  • 52. The Receive Wrapper def derivedReceiveWrapper(wrapped: Receive): Receive = { case Envelope(_, _, _, workId, _, messageNum, message) => currentWorkIdVar = workId currentMessageNumVar = messageNum wrapped(message) case message => currentWorkIdVar = createWorkId() currentMessageNumVar = MessageNum(-1) wrapped(message) } Ensures that the values that vary (workId and msgNum) are updated in the implicit scope. Friday, 11 October, 13
  • 53. Envelope Implicits trait EnvelopeImplicits { import scala.language.implicitConversions implicit def any2Envelope(a: Any) (implicit fromCompType: ComponentType, fromCompId: ComponentId, workId: WorkId, msgNum: MsgNum) = Envelope(fromCompType, fromCompId, unknownCompId, MsgType(a.getClass.getSimpleName), workId, msgNum, a) } Allows us to substitute a concrete Envelope value where an Any has been supplied. The implicit parameters make this possible. Friday, 11 October, 13
  • 54. ActorRef Implicits trait ActorRefImplicits { implicit class PimpedActorRef(ref: ActorRef) { def emit(envelope: Envelope) (implicit sender: ActorRef = Actor.noSender): Unit = { ref.tell(envelope.copy( toComponentId = ComponentId(ref.path), msgNum = envelope.msgNum.increment ), sender) } } } The emit method demands an Envelope. When you call emit, that starts the implicit conversion! any2Envelope creates it, and we update it here with better values. Friday, 11 October, 13
  • 55. Using the Protocol class MyActor extends EnvelopingActor { def derivedRecieve: Receive = { case Message(str) => someOtherActor emit Message(s”I got: $str”) } } Friday, 11 October, 13
  • 56. Using the Protocol class MyActor extends EnvelopingActor { def derivedRecieve: Receive = { case Message(str) => someOtherActor emit Message(s”I got: $str”) } } Envelope( ComponentType(“SomeActor”), ComponentType(“MyActor”), ComponentId(“/user/myactor”), WorkId(“ad7e877e41ff32a2”), MsgType(“org.my.Message”), MsgNum(0), Message(“SomeActor sent”)) Incoming Friday, 11 October, 13
  • 57. Using the Protocol class MyActor extends EnvelopingActor { def derivedRecieve: Receive = { case Message(str) => someOtherActor emit Message(s”I got: $str”) } } Envelope( ComponentType(“SomeActor”), ComponentType(“MyActor”), ComponentId(“/user/myactor”), WorkId(“ad7e877e41ff32a2”), MsgType(“org.my.Message”), MsgNum(0), Message(“SomeActor sent”)) Incoming Envelope( ComponentType(“MyActor”), ComponentType(“SomeOtherActor”), ComponentId(“/user/someother”), WorkId(“ad7e877e41ff32a2”), MsgType(“org.my.Message”), MsgNum(1), Message(“I got: SomeActor sent”)) Outgoing Friday, 11 October, 13
  • 58. Using the Protocol class MyActor extends EnvelopingActor { def derivedRecieve: Receive = { case Message(str) => someOtherActor emit Message(s”I got: $str”) } } Envelope( ComponentType(“SomeActor”), ComponentType(“MyActor”), ComponentId(“/user/myactor”), WorkId(“ad7e877e41ff32a2”), MsgType(“org.my.Message”), MsgNum(0), Message(“SomeActor sent”)) Incoming Envelope( ComponentType(“MyActor”), ComponentType(“SomeOtherActor”), ComponentId(“/user/someother”), WorkId(“ad7e877e41ff32a2”), MsgType(“org.my.Message”), MsgNum(1), Message(“I got: SomeActor sent”)) Outgoing Chained Friday, 11 October, 13
  • 59. Using the Protocol class MyActor extends EnvelopingActor { def derivedRecieve: Receive = { case Message(str) => someOtherActor emit Message(s”I got: $str”) } } Envelope( ComponentType(“SomeActor”), ComponentType(“MyActor”), ComponentId(“/user/myactor”), WorkId(“ad7e877e41ff32a2”), MsgType(“org.my.Message”), MsgNum(0), Message(“SomeActor sent”)) Incoming Envelope( ComponentType(“MyActor”), ComponentType(“SomeOtherActor”), ComponentId(“/user/someother”), WorkId(“ad7e877e41ff32a2”), MsgType(“org.my.Message”), MsgNum(1), Message(“I got: SomeActor sent”)) Outgoing Maintained Friday, 11 October, 13
  • 60. Using the Protocol class MyActor extends EnvelopingActor { def derivedRecieve: Receive = { case Message(str) => someOtherActor emit Message(s”I got: $str”) } } Envelope( ComponentType(“SomeActor”), ComponentType(“MyActor”), ComponentId(“/user/myactor”), WorkId(“ad7e877e41ff32a2”), MsgType(“org.my.Message”), MsgNum(0), Message(“SomeActor sent”)) Incoming Envelope( ComponentType(“MyActor”), ComponentType(“SomeOtherActor”), ComponentId(“/user/someother”), WorkId(“ad7e877e41ff32a2”), MsgType(“org.my.Message”), MsgNum(1), Message(“I got: SomeActor sent”)) Outgoing Incremented Friday, 11 October, 13
  • 61. All the Implicits Friday, 11 October, 13
  • 62. All the Implicits Pimp ActorRef with Emit Friday, 11 October, 13
  • 63. All the Implicits Pimp ActorRef with Emit Convert Any to envelope Friday, 11 October, 13
  • 64. All the Implicits Pimp ActorRef with Emit Convert Any to envelope Simplify the API Friday, 11 October, 13
  • 65. All the Implicits Pimp ActorRef with Emit Convert Any to envelope Simplify the API actor emit Message(”Here’s a message”) Just in case you forgot... Friday, 11 October, 13
  • 66. All the Implicits Pimp ActorRef with Emit Convert Any to envelope Simplify the API actor emit Message(”Here’s a message”) Just in case you forgot... SIMPLE Friday, 11 October, 13
  • 67. The Last Use Case Friday, 11 October, 13
  • 68. The Last Use Case Implicits help you put complexity where it belongs... In your libraries! &Away from your Users! Friday, 11 October, 13
  • 69. Pitfalls &Rules of Thumb Friday, 11 October, 13
  • 70. Pitfalls &Rules of Thumb Use very specific types ComponentType(s: String) String()NOT Friday, 11 October, 13
  • 71. Pitfalls &Rules of Thumb Use very specific types ComponentType(s: String) String()NOT Enable by import, not compiler switches Keeps libraries self-contained and avoids surprises Friday, 11 October, 13
  • 72. Pitfalls &Rules of Thumb Use very specific types ComponentType(s: String) String()NOT Enable by import, not compiler switches Keeps libraries self-contained and avoids surprises Push parameters to methods if you can This keeps implicit resolution more flexible Friday, 11 October, 13
  • 73. Pitfalls &Rules of Thumb Use very specific types ComponentType(s: String) String()NOT Enable by import, not compiler switches Keeps libraries self-contained and avoids surprises Push parameters to methods if you can This keeps implicit resolution more flexible Use the right tool for the right job!! Friday, 11 October, 13
  • 74. Implicits Scalain Derek Wyatt Twitter: @derekwyatt Email: derek@derekwyatt.org Thanks to @heathermiller for the presentation style Source code is available at: https://github.com/primal-github/implicit-messaging Friday, 11 October, 13