Akka typed-channels

26
Dr. Roland Kuhn @rolandkuhn Typed Channels and Macros söndag 10 februari 13

description

These slides are for my presentation at North-East Scala Symposium 2013 in Philadelphia, see http://nescala.org. The video is available at http://www.youtube.com/watch?v=PCk2SHufw3E

Transcript of Akka typed-channels

Page 1: Akka typed-channels

Dr. Roland Kuhn@rolandkuhn

Typed Channelsand

Macros

söndag 10 februari 13

Page 2: Akka typed-channels

someActor ! CommandOne

The Problem

söndag 10 februari 13

Page 3: Akka typed-channels

trait Commandcase class CommandOne(param: String) extends Command

someActor ! CommandOne

The Problem

söndag 10 februari 13

Page 4: Akka typed-channels

because the other does not compile

someActor <-!- CommandOne(”msg”)

The Vision

söndag 10 februari 13

Page 5: Akka typed-channels

But How?

• ActorRef must know about message types– Actor type must be parameterized

• Message type is verified against that

söndag 10 februari 13

Page 6: Akka typed-channels

val f: Future[Response] =

someActor <-?- CommandOne(”hello”)

because the compiler knows

And the replies?

söndag 10 februari 13

Page 7: Akka typed-channels

And How This?

• ActorRef must know reply types– Actor must be parameterized with them

• Reply types are extracted at call site

söndag 10 februari 13

Page 8: Akka typed-channels

No Type Pollution

• Generic Filter/Transform Actors– accept management commands– pass on generic other type

• Using just one type is not enough!

• Need to use type unions and allow multiple possible reply types for one input

söndag 10 februari 13

Page 9: Akka typed-channels

msg -?-> firstActor -?-> secondActor -!-> client

msg -?-> someService -*-> (_ map httpOk) -!-> client

Process wiring from the outside

Actors Do Compose

söndag 10 februari 13

Page 10: Akka typed-channels

The Result:

söndag 10 februari 13

Page 11: Akka typed-channels

Type-Safe Composability

of

Actor Systems

söndag 10 februari 13

Page 12: Akka typed-channels

Relation to π-calculus

• Actors are composite processes

• Actor types are structural, not nominal

• wiring A⌒B can be done situationally

http://doc.akka.io/ see Typed Channels

söndag 10 februari 13

Page 13: Akka typed-channels

That was theEloi world

Now we’re going to visit the Morlocks

söndag 10 februari 13

Page 14: Akka typed-channels

The Implementation

• Tagged type union with:+:[(In, Out), ChannelList] <: ChannelList

• Value class ChannelRef[…](val a: ActorRef)

• Actor mixin Channels[…]

• WrappedMessage[…, LUB](val m: LUB)

• ops desugar to tell/ask after type check

söndag 10 februari 13

Page 15: Akka typed-channels

class OpinionatedEcho extends Actor with Channels[TNil, (String, String) :+: TNil] {

channel[String] { (str, sender) ⇒ sender <-!- str } // or channel[String] { case (”hello”, sender) ⇒ sender <-!- ”world” case (x, sender) ⇒ sender <-!- s”dunno: $x” }

}

“sender” will accept only String messages

How to Declare it?

söndag 10 februari 13

Page 16: Akka typed-channels

First Problem: Lambdas

• Type-checker transforms lambda before the macro call– pattern matches or PartialFunction literals

generate checks depending on static type info

• Behavior is not an argument to “channels”

• macro only emits object with right “apply”

söndag 10 februari 13

Page 17: Akka typed-channels

private class Behaviorist[-R, Ch: ru.TypeTag]( wrapped: Boolean) extends (R ⇒ Unit) { // ... def apply(recv: R): Unit = { val tt = ru.typeTag[Ch] behavior ++= ( for (t ← inputChannels(ru)(tt.tpe)) yield tt.mirror.runtimeClass(t.widen) -> ff(recv)) } }

calling channels[_] registers the behavior

First Problem: Lambdas

söndag 10 februari 13

Page 18: Akka typed-channels

def impl[LUB, ReplyChannels <: ChannelList, MsgTChan <: ChannelList, MsgT: c.WeakTypeTag, MyCh <: ChannelList: c.WeakTypeTag, ParentCh <: ChannelList: c.WeakTypeTag]( c: Context { type PrefixType = Channels[ParentCh, MyCh] }): c.Expr[(Nothing ⇒ Unit)] = { // some type calculations happen here val prepTree = reify(...) reify { prepTree.splice c.prefix.splice.behaviorist[ (MsgT, ChannelRef[ReplyChannels]) ⇒ Unit, MsgT]( bool(c, false).splice)(universe.typeTag[MsgT]) } }

The Gory Details

söndag 10 februari 13

Page 19: Akka typed-channels

trait Channels[P <: ChannelList, C <: ChannelList] { this: Actor ⇒

def channel[T]: (Nothing ⇒ Unit) = macro macros.Channel.impl[Any, ChannelList, ChannelList, T, C, P]

def behaviorist[R, Ch: ru.TypeTag](wrapped: Boolean) : (R ⇒ Unit) = new Behaviorist[R, Ch](wrapped)

// ... }

The Gory Details

söndag 10 februari 13

Page 20: Akka typed-channels

final def missingChannels(u: Universe)( channels: u.Type, required: List[u.Type]): List[u.Type] = { import u._ // making the top-level method recursive blows up the compiler def rec(ch: Type, req: List[Type]): List[Type] = { ch match { case TypeRef(_, _, TypeRef(_, _, in :: _) :: tail :: Nil) ⇒ rec(tail, req filterNot (_ <:< in)) case last ⇒ req filterNot (_ <:< last) } } rec(channels, required) }

Sample Type Calculation

söndag 10 februari 13

Page 21: Akka typed-channels

def impl[..., ReplyChannels <: ChannelList, ...] (c: Context): c.Expr[(Nothing ⇒ Unit)] = { // calculate “channels: u.Type” and then: implicit val ttReplyChannels = c.TypeTag[ReplyChannels](channels) reify { ...[(..., ...[ReplyChannels]) ⇒ ..., ...]... } }

“reify” picks up TypeTags and uses their .tpe

How to Return a Type

söndag 10 februari 13

Page 22: Akka typed-channels

final def x(u: Universe)(list: u.Type, msg: u.Type) : List[u.Type] = { val imp = u.mkImporter(ru) val tpeReplyChannels = imp.importType(ru.typeOf[ReplyChannels[_]]) val tpeTNil = imp.importType(ru.typeOf[TNil]) // ... }

otherwise there will be weird exceptions

Sharing Code with Runtime

söndag 10 februari 13

Page 23: Akka typed-channels

def mkToolbox(compileOptions: String = "") : ToolBox[_ <: scala.reflect.api.Universe] = { val m = scala.reflect.runtime.currentMirror m.mkToolBox(options = compileOptions) }

def eval(code: String, compileOptions: String = "-cp akka-actor/target/classes:akka-channels/target/classes") : Any = { val tb = mkToolbox(compileOptions) tb.eval(tb.parse(code)) }

How to test it?

söndag 10 februari 13

Page 24: Akka typed-channels

intercept[ToolBoxError] { eval(""" import akka.channels._ import ChannelSpec._ implicit val c = new ChannelRef[TNil](null) new ChannelRef[(A, C) :+: TNil](null) <-!- B """) }.message must include( "target ChannelRef does not support messages of " + "types akka.channels.ChannelSpec.B.type")

How to test it?

söndag 10 februari 13

Page 25: Akka typed-channels

get it and learn morehttp://akka.io

http://typesafe.com

http://letitcrash.com

söndag 10 februari 13

Page 26: Akka typed-channels

E0Fsöndag 10 februari 13