Composition
| Surjective composition: the first function need not be surjective. (Photo credit: Wikipedia) |
In Haskell you can use this beautiful syntax
compile = parse . check . optimize . codegenleaving out the parameter(as it can be infered) and noting composition as "." which kinda looks like ° used in math(if you squint a bit).
In scala you could do something like
val compile = parse compose check compose optimize compose codegenNearly there, I just want it to look a bit prettier. (Compose is a method defined on Function1 trait)
So I define an implicit conversion from function to "composable"
implicit def function2composable[A,B](f: A=>B) = new AnyRef{
def -->[C](g: B=>C) = v => g(f(c))
}
This creates an object and reimplements "compose" but I really like the syntax:
compile = parse --> check --> optimize --> codegenFunctional programming can be imagined as a waterfall of data, flowing from one function into the next, and the --> operator represents this nicely.
Let's go a step further. If the composition is one-off, and this is a waterfall of DATA it could be nice to represent that.
Something like
result = source --> parse --> check --> optimize --> codegenSo now I'm taking a value and sending it through black boxes. Very nice. Apart from the fact it doesn't work(yet!).
implicit def any2waterfall[A](a: A) = new AnyRef{
def -->[B](f: A=>B) = f(a)
}
Scala's awesome compiler can handle two implicit conversions with same method names. Nice.You can even mix and match
result = source --> (parse --> check --> optimize) --> codegenThis does the composition of parse, check and optimize into an anonymous function, applies it to the source and then applies codegen to it's result.
Goin async
| Image via CrunchBase |
What about asynchronous calls? Can I compose those too? I think it's possible with Lift actors(or with scalaz's?), but I needed to integrate that into Android's activities quite recently. Well I did not *need* to do it, but it was quite a nice solution.
The usual way of doing things async in Android is with the conveniently named AsyncTask. The problem is - you can't subclass it in scala because of some compiler bug regarding varargs parameters. Silly.
So let's do a lightweight(in terms of code) substitution. We can "spawn a thread" using scala's actors. And activity can receive messages through a Handler.
import android.os.{Message, Handler}
trait MessageHandler {
private val handler = new Handler {
override def handleMessage(msg: Message) {
react(msg.obj)
}
}
def react(msg: AnyRef)
def !(message: AnyRef) {
val msg = new Message
msg.obj = message
handler.sendMessage(msg)
}
}
So in an activity that mixes in MessageHandler I can post messages to my self. An I can do it async since Handler is thread safe
def react(msg: AnyRef) = msg match {
case s: String = Toast(this, s, Toast.LENGTH_LONG).makeText()
case _ => ()
}
...
//somewhere inside UI thread
import actors.Actor.actor
val that = this
actor{
val msg = doExpensiveWork()
that ! msg
}
...
Not the most concise way to write it, but I believe the most clear one. Method doExpensiveWork is done in the background so it doesn't block UI and it posts the result back as a message.Async composition - finally
What I want to do now is use function composition to do something like
I need a new trait for that
(input --> expensiveOne --> expensiveTwo) -!> displayResultsIn other words, do "waterfall composition" in background using some input I have now and post the result back into the UI thread to method displayResults. That should be the magic of the -!> operator. Do the left side in bg and post it to the right side async.
I need a new trait for that
trait MessageHandlerExec extends MessageHandler{ outer =>
override protected val handler = new Handler {
override def handleMessage(msg: Message) = msg.obj match {
case r: Runnable => r.run()
case other: AnyRef => react(other)
}
}
implicit def any2asyncComposable[A](a: => A) = new AnyRef{
def -!>[B](f: A=>B) = outer ! new Runnable{
def run() = f(a)
}
}
}
The trick here is using by-name parameters in the implicit conversion. This delays the execution of a(which in example above would be a waterfall and moves it into a worker thread.






No comments:
Post a Comment