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