Upload
derek-wyatt
View
9.530
Download
0
Tags:
Embed Size (px)
DESCRIPTION
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.
Citation preview
AgendaLies and Damn LiesUse CasesScopeExampleCode!
Truths
Rules of Thumb
Friday, 11 October, 13
Implicits are NEW
Friday, 11 October, 13
Implicits are NEW
New things can be cool!
Friday, 11 October, 13
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
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
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
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
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
Implicits
Friday, 11 October, 13
ImplicitsAre Not
Friday, 11 October, 13
ImplicitsAre Not
Global variables LIE!
Friday, 11 October, 13
ImplicitsAre Not
Global variablesDynamically Applied
LIE!
DAMN
LIE!
Friday, 11 October, 13
ImplicitsAre Not
Global variablesDynamically AppliedDangerous (...much...)
LIE!
DAMN
LIE!
Fib
Friday, 11 October, 13
ImplicitsAre Not
Global variablesDynamically AppliedDangerous (...much...)
Implicits are like scissors.Use them. Don’t run with them.
LIE!
DAMN
LIE!
Fib
Friday, 11 October, 13
Use CasesWhat are they used for?
Friday, 11 October, 13
Use Case: Type Classes
Friday, 11 October, 13
Use Case: Type Classes
val a = someInt()val b = someOtherInt()val lesser = if (lessThan(a, b)) a else b
Friday, 11 October, 13
Use Case: Type Classes
val a = someInt()val b = someOtherInt()val lesser = if (lessThan(a, b)) a else b
lessThan needs definition
Friday, 11 October, 13
Use Case: Type Classesdef 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 needs definition
Friday, 11 October, 13
Use Case: Type Classesdef 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 gotta come from somewhere
lessThan needs definition
Friday, 11 October, 13
Use Case: Type Classesdef 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 gotta come from somewhere
lessThan needs definition
Friday, 11 October, 13
Use Case: Type Classesdef 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 gotta come from somewhere
val a = someInt()val b = someOtherInt()val lesser = if (lessThan(a, b)(intOrdering)) a else b
lessThan needs definition
Friday, 11 October, 13
Use Case: Class Extension
Friday, 11 October, 13
Use Case: Class Extensionval hexVals = “implicit”.asHexSeq// Vector(0x69, 0x6D, 0x70, 0x6C, 0x69, 0x63, 0x69, 0x74)
The “Pimp”
Friday, 11 October, 13
Use Case: Class Extensionval 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
Use Case: Class Extensionval 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
Use Case: Declutteringval future1 = someCall(“a parameter”, 5.seconds)val future2 = someCall(345, 5.seconds)val future3 = someCall(235.9352, 5.seconds)
Friday, 11 October, 13
Use Case: Declutteringval future1 = someCall(“a parameter”, 5.seconds)val future2 = someCall(345, 5.seconds)val future3 = someCall(235.9352, 5.seconds)
Blurgh
Friday, 11 October, 13
Use Case: Declutteringval 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
Use Case: Declutteringval 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
Use Case: Internal DSLsCreate your own sub-language with ease
Friday, 11 October, 13
Use Case: Internal DSLsCreate 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
Use Case: Internal DSLsCreate 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
Use Case: Other stuff...
Friday, 11 October, 13
Use Case: Other stuff...Overriding defaults
def func[A](a: A)(implicit tout: Duration = 3.seconds): Future[A]
Friday, 11 October, 13
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
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
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
Implicit ScopeRules!!!!
Friday, 11 October, 13
Implicit ScopeRules!!!!
There are a Lot of Rules
Friday, 11 October, 13
Implicit ScopeRules!!!!
There are a Lot of RulesRead “Scala In Depth”*
*Josh SuerethFriday, 11 October, 13
Implicit ScopeRules!!!!
There are a Lot of RulesRead “Scala In Depth”*Implicits without the Import Tax*
*Josh SuerethFriday, 11 October, 13
Implicit ScopeRules!!!!
There are a Lot of RulesRead “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
Creating a Protocol
Friday, 11 October, 13
Creating a Protocolan Implicit
Friday, 11 October, 13
Creating a Protocolan Implicit
We want:actor emit Message(“Hello”)
Friday, 11 October, 13
Creating a Protocolan 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
An Actor Derivationtrait 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
An Actor Derivationtrait 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
The Receive Wrapperdef 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
The Receive Wrapperdef 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
Envelope Implicitstrait 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
ActorRef Implicitstrait 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
Using the Protocolclass MyActor extends EnvelopingActor { def derivedRecieve: Receive = { case Message(str) => someOtherActor emit Message(s”I got: $str”) }}
Friday, 11 October, 13
Using the Protocolclass 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
Using the Protocolclass 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
Using the Protocolclass 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”))
OutgoingChained
Friday, 11 October, 13
Using the Protocolclass 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
Using the Protocolclass 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
All the Implicits
Friday, 11 October, 13
All the ImplicitsPimp ActorRef with Emit
Friday, 11 October, 13
All the ImplicitsPimp ActorRef with Emit
Convert Any to envelope
Friday, 11 October, 13
All the ImplicitsPimp ActorRef with Emit
Convert Any to envelope
Simplify the API
Friday, 11 October, 13
All the ImplicitsPimp ActorRef with Emit
Convert Any to envelope
Simplify the API
actor emit Message(”Here’s a messa
ge”)
Just in case you forgot...
Friday, 11 October, 13
All the ImplicitsPimp ActorRef with Emit
Convert Any to envelope
Simplify the API
actor emit Message(”Here’s a messa
ge”)
Just in case you forgot...SIMPLE
Friday, 11 October, 13
The Last Use Case
Friday, 11 October, 13
The Last Use Case
Implicits help you put complexity
where it belongs...
In your libraries!&Away from your Users!Friday, 11 October, 13
Pitfalls&Rules of Thumb
Friday, 11 October, 13
Pitfalls&Rules of ThumbUse very specific types ComponentType(s: String) String()NOT
Friday, 11 October, 13
Pitfalls&Rules of ThumbUse very specific types ComponentType(s: String) String()NOT
Enable by import, not compiler switchesKeeps libraries self-contained and avoids surprises
Friday, 11 October, 13
Pitfalls&Rules of ThumbUse very specific types ComponentType(s: String) String()NOT
Enable by import, not compiler switchesKeeps libraries self-contained and avoids surprises
Push parameters to methods if you canThis keeps implicit resolution more flexible
Friday, 11 October, 13
Pitfalls&Rules of ThumbUse very specific types ComponentType(s: String) String()NOT
Enable by import, not compiler switchesKeeps libraries self-contained and avoids surprises
Push parameters to methods if you canThis keeps implicit resolution more flexible
Use the right tool for the right job!!
Friday, 11 October, 13
ImplicitsScalain
Derek WyattTwitter: @derekwyatt
Email: [email protected]
Thanks to @heathermiller for the presentation style
Source code is available at:https://github.com/primal-github/implicit-messaging
Friday, 11 October, 13