A Googler and a Facebooker were in a pub discussing the complexities of building out a rich modern Web application. There are a ton of dependencies, and you need to be proficient in multiple languages and tools (JavaScript, HTML, CSS, SQL/NoSQL, backend languages, build tools, etc).
Well, they may not have been in a pub.... but a deadly duo did get together to try to solve this problem.
Dustin Moskovitz (Facebook co-founder and former CTO) and former-Googler/Facebooker Justin Rosenstein decided to try something different when they started to implement a Collaborative Information Manager tool at their new startup . They have created a new high level language that is part JavaScript, part Protocol Buffers. The language is LunaScript.
What was their motivation?
All of us on the Asana team have deep backgrounds writing rich web applications at companies like Google and Facebook. We've been continually frustrated by how long it takes to write software, and by a nagging feeling that in some deep sense we've been writing the same code over and over. Even when using the latest and greatest frameworks and disciplines, writing fast, highly interacting web applications involves a lot of accidental complexity:
First you need server code to figure out what data the browser needs. Hopefully you have an ORM layer, but you still need to carefully structure your code to minimize your backend dispatches, and you need to carefully keep that in sync with your front-end code lest you don't fetch enough data or hurt performance by fetching too much. If it's a Web 2.0-style app, you re-implement a ton of that server-side code in JavaScript, once for creating the page and then again as controller code for keeping it up to date and consistent. And when the user changes something, you bottle that up -- typically in a custom over-the-wire format -- and send it as an XHR to the server. The server has to de-serialize it into different data structures in a different language, notify the persistence store, figure out what other clients care about the change, and send them a notification over your Comet pipe, which is handled by yet more JavaScript controller code. Offline support? More code.
This is not a one-off task: it's rote work that adds complexity to every feature that you build in every application. By the time that you are done with all this, the important and novel parts of your application are only around 10% of your code. We wondered whether we could build a programming system in which we just wrote that 10% -- the essential complexity -- and a compiler handled the other 90%.
And this lead them to their solution:
Inspired by incremental computing, we're building Lunascript as a simple way to write modern web applications. Lunascript has a syntax and easy of use reminiscent of JavaScript, but a powerful pure-functional lazily-evaluated semantics historically confined to academic languages.
A Lunascript application specifes a data model and a function from the model to the view or user interface, annotated with handler functions from user inputs to model mutations. From this the Lunascript compiler produces a functioning Web2.0 application -- the client-side JavaScript, the server-side SQL, and everything in between -- complete with real-time bidirectional data synchronization. There's no need to write separate code to help the server figure out which values need to be sent to the client: the server can do this by simulating the UI. Because a LunaScript application only specifies how the UI should look given the current data (rather than how the UI should be updated as changes happen) it's impossible to write a UI that loads correctly but does not stay correct as changes are made.
"What does it look like?" I hear you cry. Time for a hello world, and since Comet is in the mix, that means a chat server:
JAVASCRIPT:
-
-
// Some of our currently implemented syntax isn't quite this clean, but it's
-
// fairly close and this is the direction we're going.
-
-
class World {
-
// "1." serves the same purpose as "= 1" does in Google protocol buffer syntax.
-
1. ChatMessage[] messages;
-
};
-
-
class ChatMessage {
-
1. User user;
-
2. string text;
-
};
-
-
class Session {
-
1. User user;
-
2. String new_comment;
-
};
-
-
return fn(world, browser, session) {
-
var renderMessage = fn(message) {
-
// Most style information omitted to improve readability.
-
var bubble_style = { background: '#b7e0ff', padding: 7, ... };
-
-
return <div> // XML literals are first-class primitives.
-
<img src=message.user.small_pic_url />
-
<div style=bubble_style>
-
<b> message.user.name, ': ' </b>
-
<div> message.text </div>
-
</div>
-
</div>;
-
};
-
-
var postMessage = fn() {
-
// Only handler functions can request data mutations.
-
messages += ChatMessage {
-
user: session.user,
-
text: session.new_comment
-
};
-
session.new_comment := '';
-
};
-
-
return <table>
-
<tr><td>
-
messages.map(renderMessage)
-
</td></tr>
-
<tr><td>
-
<img src=(session.user.small_pic_url) />
-
<div>
-
<input data=session.user.name /> <b>' (your nickname)'</b>
-
<form onsubmit=postMessage>
-
<input data=session.new_comment hasFocus=true />
-
</form>
-
</div>
-
</td></tr>
-
</table>;
-
};
-
You will notice the protobufferness at the top, and the fn of less characters, and E4X-like.
To learn more, let's listen in to Dustin as he gives us a walk through the world of LunaScript:
I talked to the guys and asked a few questions which they answered..... what questions do you have for them though? Are you excited about what a higher level abstraction could give you? Or do you like to be close to the metal?