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.
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
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
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
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
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
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
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
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
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