74
Implicits Scala in Derek Wyatt Twitter: @derekwyatt Email: [email protected] Friday, 11 October, 13

Scala Implicits - Not to be feared

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

Page 1: Scala Implicits - Not to be feared

ImplicitsScalain

Derek WyattTwitter: @derekwyatt

Email: [email protected], 11 October, 13

Page 2: Scala Implicits - Not to be feared

AgendaLies and Damn LiesUse CasesScopeExampleCode!

Truths

Rules of Thumb

Friday, 11 October, 13

Page 3: Scala Implicits - Not to be feared

Implicits are NEW

Friday, 11 October, 13

Page 4: Scala Implicits - Not to be feared

Implicits are NEW

New things can be cool!

Friday, 11 October, 13

Page 5: Scala Implicits - Not to be feared

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

Page 6: Scala Implicits - Not to be feared

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

Page 7: Scala Implicits - Not to be feared

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

Page 8: Scala Implicits - Not to be feared

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

Page 9: Scala Implicits - Not to be feared

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

Page 10: Scala Implicits - Not to be feared

Implicits

Friday, 11 October, 13

Page 11: Scala Implicits - Not to be feared

ImplicitsAre Not

Friday, 11 October, 13

Page 12: Scala Implicits - Not to be feared

ImplicitsAre Not

Global variables LIE!

Friday, 11 October, 13

Page 13: Scala Implicits - Not to be feared

ImplicitsAre Not

Global variablesDynamically Applied

LIE!

DAMN

LIE!

Friday, 11 October, 13

Page 14: Scala Implicits - Not to be feared

ImplicitsAre Not

Global variablesDynamically AppliedDangerous (...much...)

LIE!

DAMN

LIE!

Fib

Friday, 11 October, 13

Page 15: Scala Implicits - Not to be feared

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

Page 16: Scala Implicits - Not to be feared

Use CasesWhat are they used for?

Friday, 11 October, 13

Page 17: Scala Implicits - Not to be feared

Use Case: Type Classes

Friday, 11 October, 13

Page 18: Scala Implicits - Not to be feared

Use Case: Type Classes

val a = someInt()val b = someOtherInt()val lesser = if (lessThan(a, b)) a else b

Friday, 11 October, 13

Page 19: Scala Implicits - Not to be feared

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

Page 20: Scala Implicits - Not to be feared

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

Page 21: Scala Implicits - Not to be feared

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

Page 22: Scala Implicits - Not to be feared

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

Page 23: Scala Implicits - Not to be feared

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

Page 24: Scala Implicits - Not to be feared

Use Case: Class Extension

Friday, 11 October, 13

Page 25: Scala Implicits - Not to be feared

Use Case: Class Extensionval hexVals = “implicit”.asHexSeq// Vector(0x69, 0x6D, 0x70, 0x6C, 0x69, 0x63, 0x69, 0x74)

The “Pimp”

Friday, 11 October, 13

Page 26: Scala Implicits - Not to be feared

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

Page 27: Scala Implicits - Not to be feared

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

Page 28: Scala Implicits - Not to be feared

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

Page 29: Scala Implicits - Not to be feared

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

Page 30: Scala Implicits - Not to be feared

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

Page 31: Scala Implicits - Not to be feared

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

Page 32: Scala Implicits - Not to be feared

Use Case: Internal DSLsCreate your own sub-language with ease

Friday, 11 October, 13

Page 33: Scala Implicits - Not to be feared

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

Page 34: Scala Implicits - Not to be feared

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

Page 35: Scala Implicits - Not to be feared

Use Case: Other stuff...

Friday, 11 October, 13

Page 36: Scala Implicits - Not to be feared

Use Case: Other stuff...Overriding defaults

def func[A](a: A)(implicit tout: Duration = 3.seconds): Future[A]

Friday, 11 October, 13

Page 37: Scala Implicits - Not to be feared

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

Page 38: Scala Implicits - Not to be feared

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

Page 39: Scala Implicits - Not to be feared

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

Page 40: Scala Implicits - Not to be feared

Implicit ScopeRules!!!!

Friday, 11 October, 13

Page 41: Scala Implicits - Not to be feared

Implicit ScopeRules!!!!

There are a Lot of Rules

Friday, 11 October, 13

Page 42: Scala Implicits - Not to be feared

Implicit ScopeRules!!!!

There are a Lot of RulesRead “Scala In Depth”*

*Josh SuerethFriday, 11 October, 13

Page 43: Scala Implicits - Not to be feared

Implicit ScopeRules!!!!

There are a Lot of RulesRead “Scala In Depth”*Implicits without the Import Tax*

*Josh SuerethFriday, 11 October, 13

Page 44: Scala Implicits - Not to be feared

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

Page 45: Scala Implicits - Not to be feared

Creating a Protocol

Friday, 11 October, 13

Page 46: Scala Implicits - Not to be feared

Creating a Protocolan Implicit

Friday, 11 October, 13

Page 47: Scala Implicits - Not to be feared

Creating a Protocolan Implicit

We want:actor emit Message(“Hello”)

Friday, 11 October, 13

Page 48: Scala Implicits - Not to be feared

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

Page 49: Scala Implicits - Not to be feared

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

Page 50: Scala Implicits - Not to be feared

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

Page 51: Scala Implicits - Not to be feared

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

Page 52: Scala Implicits - Not to be feared

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

Page 53: Scala Implicits - Not to be feared

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

Page 54: Scala Implicits - Not to be feared

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

Page 55: Scala Implicits - Not to be feared

Using the Protocolclass MyActor extends EnvelopingActor { def derivedRecieve: Receive = { case Message(str) => someOtherActor emit Message(s”I got: $str”) }}

Friday, 11 October, 13

Page 56: Scala Implicits - Not to be feared

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

Page 57: Scala Implicits - Not to be feared

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

Page 58: Scala Implicits - Not to be feared

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

Page 59: Scala Implicits - Not to be feared

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

Page 60: Scala Implicits - Not to be feared

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

Page 61: Scala Implicits - Not to be feared

All the Implicits

Friday, 11 October, 13

Page 62: Scala Implicits - Not to be feared

All the ImplicitsPimp ActorRef with Emit

Friday, 11 October, 13

Page 63: Scala Implicits - Not to be feared

All the ImplicitsPimp ActorRef with Emit

Convert Any to envelope

Friday, 11 October, 13

Page 64: Scala Implicits - Not to be feared

All the ImplicitsPimp ActorRef with Emit

Convert Any to envelope

Simplify the API

Friday, 11 October, 13

Page 65: Scala Implicits - Not to be feared

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

Page 66: Scala Implicits - Not to be feared

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

Page 67: Scala Implicits - Not to be feared

The Last Use Case

Friday, 11 October, 13

Page 68: Scala Implicits - Not to be feared

The Last Use Case

Implicits help you put complexity

where it belongs...

In your libraries!&Away from your Users!Friday, 11 October, 13

Page 69: Scala Implicits - Not to be feared

Pitfalls&Rules of Thumb

Friday, 11 October, 13

Page 70: Scala Implicits - Not to be feared

Pitfalls&Rules of ThumbUse very specific types ComponentType(s: String) String()NOT

Friday, 11 October, 13

Page 71: Scala Implicits - Not to be feared

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

Page 72: Scala Implicits - Not to be feared

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

Page 73: Scala Implicits - Not to be feared

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

Page 74: Scala Implicits - Not to be feared

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