You can download the latest jar of the framework here. Just stick it in your project folder. I'd personally recommend buildr for building and compiling Scala projects. If you're wanting to hack on the source you can get that here.
Functional Reactive Programming is concerned with programming with two new types: Behaviours and Events. Behaviours are continuous values that change over time and Events are infinite streams of values that 'occur' at a discrete point in time - we can think of them as as a stream of time and value pairs. Mental eh?
Lets see some examples. Here we're going to make a simple Behaviour:
val beh = Behaviour(time => time * 2)
This Behaviour will, at a given time t
have the value t * 2
. So how do we actually
get at that value? We use the eval
function:
val value = beh.eval() println(value)
The above code would evaluate the Behaviour at the current time and then print the calculated value out.
So what about Events? Well here's something. Imagine we have an Event mouseEv
that occurs every time the left mouse button is pressed. We could construct a Behaviour like this:
val false = Behaviour(time => false) val true = Behaviour(time => true) val beh = false.until(mouseEv, true)
This creates a Behaviour that has the value false
until the left mouse button is
pressed, at which point its value is true
. Cool? How about this:
val beh = false.toggle(mouseEv, true)
Guess what this does? Thats right. It produced a Behaviour that toggles between false
and true
whenever that crazy left mouse button is pressed.
Getting the hang of this? Lets jump in at the deep end:
val mouseTimes = mouseEv.map((t, v) => t) val lastTime = Stepper(-1, mouseTimes)
Ok. Here we map the mouse button Event to return a new Event who's values are that of the times
of each occurrence. We then use a Stepper. A Stepper is a Behaviour that has an initial value
and an Event: at any point in time its value will be the Event's last occurrence's value (or the initial
value if the Event hasn't occurred). With all that put together you will see that we've created a Behaviour
that's value will always be the last time the mouse button was pressed (or -1
to specify it
was never pressed).
That should all have hopefully got you into the right frame of mind. Lets move on to writing some code in echo...
To write echo applications you simply extend the EchoApp
class with your
code's main object. This class already has a defined main
function
that takes care of some stuff for you so all you need to do is write
your code in the setup
function like so:
object App extends EchoApp { def setup(args : Array[String]) { val beh = Behaviour(time => time * 2) val value = beh.eval() println(value) } }
Unfortunately in order to avoid the FRP semantics going bonkers you can only write echo code
inside this setup
function. This may seem restrictive but you'll find in FRP
you generally write a small amount of code in an upfront declarative manner to define
an entire applications run-time behaviour.
You might notice that a lot of the time the Behaviours we create are 'constant' (always have the same value). So we can express these easily we can just write constant Behaviours as their constant values:
val beh = 5.until(event, 5)
You are not restricted to only using the Events provided in echo. You can simply extend the EventSource class to create your own:
class CustomEvent extends EventSource[Int] { occur(5) }
This creates an Event that when instantiated occurs
with a value of 5
. The occur
function used here is only available to classes that
extends EventSource.
There is also a simple UI framework in echo so you make funky GUI apps for your users. What's cool about this is that you can set GUI components attributes to be Behaviours. Before we launch into all that though lets make a small little app with a button and some text:
val frame = Frame(200, 200, List( Text("Hello you!"), Button("Click Me!") ))
This creates a Frame with a size of 200 x 200. It should be noted that these dimensions are Behaviours so if we set them to change over time the window size would stay up to date with the Behaviour's value. So lets do something with FRP:
val button = Button("Click Me!") val text = "Hello".until(button.click, "Goodbye") val frame = Frame(200, 200, List( text, button ))
When this app runs the text component will say "Hello"
but when a user hits the button
it will change to saying "Goodbye"
. You can see pretty immediately that this allows us
to build our GUI's in a much nicer way. No ActionEventListener here. Its hard to cover
all the different components and attributes for the UI framework here but the
docs
should provide a good starting point.
Lets face it. These days its all about talking to other computers or connecting to the 'internet'. We build a couple of types to allow you to do this in Echo if you are that way inclined. These are Sender and Receiver. A Sender sends messages whenever a given Event occurs and a Receiver occurs whenever it receives a message. Lets build an example:
val button = Button("Ping") val pinger = button.click.map((t, v) => "Ping") val sender = Sender("localhost", 1997, pinger) val receiver = Receiver(1997)
Here we create a Sender that will send the message "Ping"
out to the specified
IP and port number. You can see the Receiver uses the same port and as we are on
the same computer the messages the messages will be received by the Receiver we created.
Of course we are now communicating over a network we've started writing FRP code that
might fail due to something out of our control. Luckily we can deal with this. Remember Exceptions?
Well we can the Sender and Receiver types have an errors
Event that will occur whenever an Exception
is thrown within their operation. So, if want a Behaviour that shows the last error message from our
Sender:
val error = Stepper("No errors!", sender.errors.map((t, e) => e.toString))
Using Events to deal with Exceptions should allow you to declaratively define how your code is going to deal with errors.