We're going to simulate network traffic on a machine. A machine may have any number of servers on it, each of which can attempt to handle an incoming network connection. Let's picture an web server process on that server. Let's take a first cut at modeling a "network daemon" in Scala.
trait NetworkMessage //This would be some complex object
import scala.actors.Actor
import Actor._
trait NetworkDaemon extends Actor {
def trafficHandlers : PartialFunction[Any,Unit]
def act() {
loop(react(trafficHandlers))
}
}
So why not just create the partial function for react inline? Why have a separate method that defines the function? Remember, we're trying to implement a chain of command pattern with our actor. This means our subclasses need to attempt to handle message first, and defer to us if unable. The main reason to use a "method returning a partial function" vs. actually method overriding/extending is so that we preserve the partial-function's "isDefinedAt" semantics. Because we're using actors, it is beneficial if our "handler" function does *not* work against all input, but allows things to pass through. This helps us not catch internal actors communication *and* allow things like linking actors together.
Now, let's start with modeling a web server that hosts static content.
//Extractor for staticly served content
object HttpRequestForStaticResource {
def unapply(msg : Any) : Option[StaticResource] = None //TODO - Implement
}
//WebServer basic model
trait WebServer extends NetworkDaemon {
def trafficHandlers : PartialFunction[Any,Unit] = {
case HttpRequestForStaticResource(resource) => reply(resource.getContents)
}
}
Now imagine we want two types of web servers, a 'static' server that *always* server static content, and a subclass of that which can also proxy connections to other web servers.
//Define a new extractor for proxying
object ProxyableConnection {
def unapply(...)...
}
//WebServer basic model
trait ProxyWebServer extends WebServer {
def trafficHandlers : PartialFunction[Any,Unit] = {
val myHandlers : PartialFunction[Any,Unit] = {
case ProxyableConnection(...) => forward(...)
}
myHandlers orElse super.trafficHandlers //Use our partials *first* before others
}
}
Notice the boiler-plate code here. We're chaining our partial functions in a way that makes this work like chain-of-command. All partial functions from "myHandlers" should be checked before our super-class trafficHandlers. However, to make this work, we end up with a not-quite-so-good-looking syntax. This should just show itself when defining the class hierarchy, code that uses this pattern shouldn't really be able to tell how we're implementing our chain of command pattern, e.g.
//Imagine setting up a complex simulation item that has various roles mixed in.
val webServer = new ProxyWebServer with ModularWebServer {
addModule(new CgiModule()); //In our hypothetical simulation, this simulates the overhead of making CGI calls.
}
val results = for( msg <- simulateMessages) yield {
webServer !! msg
}
//TODO - Loop through results and analyze simulation
As you can see, this can lead to some nice functionality and composability of your actor-entities.
The question remaining in my mind is "Can the boilerplate be reduced in scala". Ideally I would love to be able to do the following (for this situation):
trait NetworkDaemon extends Actor {
partial def trafficHandlers(msg : Any) : Unit //Abstract partial function
def act() {
loop(react(trafficHandlers))
}
}
trait WebServer extends NetworkDaemon {
//Implements the partial def with an implementation
partial def trafficHandlers(msg : Any) : Unit = {
case HttpRequestForStaticResource(resource) => reply(resource.getContents)
}
}
trait ServletWebServer extends WebServer {
//Completely overrides our parents partial function
override partial def trafficHandlers(msg : Any) : Unit = {
case ProxyableConnection(...) => forward(...)
}
}
trait ProxyWebServer extends WebServer {
//Adds our implementation before our parents
override partial def trafficHandlers(msg : Any) : Unit = {
case ProxyableConnection(...) => forward(...)
super //This means delegate to the partial function defined on our super class (could be qualified)
}
}
trait DevNullDaemon extends NetworkDaemon {
//Overrides the partial definition with a *complete* definition
//This could be useful for enforcing completeness at a given layer of the hierarchy.
override def trafficHandlers(msg : Any) : Unit = {
//ignore
}
}
I'm still debating the syntax on delegating to a superclass partial function, but the general gist is to allow similar mechanisms to method over-riding/chaining but instead with partial functions, as well as the ability to define partial functions as methods on a class. Currently, I only see this feature covering a niche of use cases, but it could be quite handy for those. Behind the scenes, the compiler would be desugaring the syntax into something like the original implementation.
If anyone else is interested in seeing this kind of functionality in scala, perhaps we should get together and produce a SIP (after fixing up the "delegating to super" syntax...). I've debated trying to accomplish this with a compiler plugin and annotations, and I still may go that route for a proof of concept. (Granted, this all requires enough free time to actually finish one of my side projects!).
0 comments:
Post a Comment