2010-12-14
Simple Ur/Web Example
I've been admiring Ur/Web from afar for a while now and I've decided to dive into it and try it out on a project.
Ur/Web is described at the website as:
Ur.
Basically it's a programming language and system for building web applications with a rich type system to prevent common web programming errors from occuring.
The compiled Ur/Web applications do not use a garbage collector and are supposed to be very efficient in RAM usage. Ur/Web can also be used for some client side programming and allows writing code that uses threads in the web browser, functional reactive programming for client side updates and RPC calls between the browser and server as well as from the server to the browser. Ur/Web has a good reference manual and an excellent online demo/tutorial.
For the project I want to write I need to be able to spawn command line processes and get the output. Marc Weber has written a library for Ur/Web that does this called uw-process.
I started with a simple example program to get a taste of Ur/Web that uses
uw-process. The application displays a single page showing the current time on the server as returned by
date. This updates the time every second using Ur/Web's functional reactive programming system.
The clock.ur file implements the application. The
getTime function uses
uw-process to call
date and return the standard output of that as a string:
fun getTime () = result <- Process.exec "date" (textBlob "") 100; return (Process.blobText (Process.blob (result)))
Process.exe takes three arguments. The command to run ("date" in this case), a text blob containing the standard input to be used by the command (an empty string here) and the maximum length of data to take from the standard output.
The
main function is what will be executed when the web application is run:
fun main () = s <- source "" ; return <xml><head> <title>Current Server Time</title> </head> <body onload={spawn (timeLoop s)}> <dyn signal={t <- signal s; return <xml>{[t]}</xml>}/> </body> </xml>
This uses functional reactive programming on the client to continously call the server asking for the time. We use Ur/Web's support for embedded XML to construct the result page:
return <xml><head> <title>Current Server Time</title> </head> <body onload={spawn (timeLoop s)}> <dyn signal={t <- signal s; return <xml>{[t]}</xml>}/> </body> </xml>
The 'onload' handler for the body of the page has this syntax:
{spawn (timeLoop s)}
spawn is used to spawn a thread in the browser. Here we spawn a thread to run the 'timeLoop' function passing it a source,
s. This is compiled into JavaScript by Ur/Web. The
timeLoop function written in Ur/Web is also compiled to JavaScript:
fun timeLoop s = t <- rpc (getTime ()); set s t; sleep 1000; timeLoop s
timeLoop does a remote procedure call to the server executing the
getTime function shown previously. The source
s is set to the result and then the JavaScript thread sleeps for one second. A tail call is made to loop back to calling timeLoop again when the second is up.
As this function is called from a JavaScript handler (
onload), it's compiled into JavaScript to run on the client side. The RPC call is converted into an HTTP request to the server to call the
getTime function and return the result.
In the body of the HTML we use the source
s in a way to display the time value so that it is automatically updated whenever it changes:
<dyn signal={t <- signal s; return <xml>{[t]}</xml>}/>
A source represents a dynamically changing value in the page. When the source changes the page changes automatically. The part that changes is the
dyn element. The
signal attribute of that element has Ur/Web code to call
signal on the source. This retrieves the value that was changed (the time in our example) and we return an XML fragment containing that value. The syntax
{[t]} coerces the value of
t to something safe to be displayed in XML, taking into account cross site scripting issues, HTML encoding, etc.
The remaining Ur/Web files are clock.urs which contains the type signature for the
main function exposed by our application, and clock.urp which is the project file.
To build the example:
$ urweb clock
This produces a
clock.exe (a strange name for the resulting file given we're on a Linux system...). Running this file will start a webserver on port 8080 that runs our application:
$ ./clock.exe
Visiting will display the server time, updated every second, in the browser.
This is obviously a pretty trivial example but does show how easy it is to do threads in the browser and RPC calls. The Ur/Web Demo's show much more including database usage and various uses of the type system.
I've put this example in github. It can be cloned, built and run with:
$ git clone git://github.com/doublec/urweb-clock $ cd urweb-clock $ git submodule init $ git submodule update $ make $ ./clock.exe
I include
uw-process as a git submodule and the makefile assumes Ur/Web is installed in
/usr/local.
I haven't written anything about the type system or other features of Ur/Web that make it interesting yet but I hope to look at some of these in later posts.