Upload
eelco-visser
View
1.013
Download
1
Tags:
Embed Size (px)
DESCRIPTION
slides from keynote for MOSE 2010 in Malaga on June 29, 2010
Citation preview
Eelco VisserZef Hemel
@mobllang @zef @eelcovisser
Slides from keynote at MOSE 2010, Malaga, June 29, 2010
Domain-Specific Language Engineering
WebDSL
SpoofaxSDF Stratego
domain:mobile applications
50 million
iPhones
20 million
iPod Touches
1.5 million
G1
1.2 million
Droid
outsells iPhone in US
applicationdevelopment
Objective-C Java J2ME/C++
HTML/Javascript Java
Objective-C
Android Java
Blackberry Java
J2ME
HTML/JS
3.3.1
3.3.1 – Applications may only use Documented APIs in the manner prescribed by Apple and must not use or call any private APIs. Applications must be originally written in Objective-C, C, C++, or JavaScript as executed by the iPhone OS WebKit engine, and only code written in C, C++, and Objective-C may compile and directly link against the Documented APIs (e.g., Applications that link to Documented APIs through an intermediary translation or compatibility layer or tool are prohibited).
AppStore
cross-platform development
arbitrary rejections
we want high-level models
Webkit
HTML
WebDatabases
Location information (GPS)
Threading
Canvas
WebDatabases
Multi-touch
Offline support
Full-screen support
We believe the web has won and over the next several years, the browser [..] will become the platform that matters and certainly that’s where Google is investing.
“ ”Vic Gundotra, Google VP of Engineering
syntax similar to
data model
user interface
script
web service access
data model
entity Task { name : String (searchable) done : Bool dueDate : DateTime}
entity Task { name : String (searchable) done : Bool dueDate : DateTime categories : Collection<Category>}
entity Category { name : String tasks : Collection<Task> (inverse: categories)}
user interface
screen root() { header("Todo") group { list(t in Task.all()) { item { checkbox(t.done) " " label(t.name) } } }}
screen root() { header("Todo") topButton("Add", onclick={ addTask(); }) group { list(t in Task.all()) { item { checkbox(t.done) " " label(t.name) } } }}
screen addTask() { var newTask = Task { done = false, dueDate = now() }
header("Add") backButton("Back", onclick={ screen return; }) group { item { textField(newTask.name) } item { datePicker(newTask.dueDate) } } button("Add", onclick={ add(newTask); screen return; })}
screen root() { header("Todo") topButton("Add", onclick={ addTask(); }) group { list(t in Task.all()) { item { checkbox(t.done) " " label(t.name) } } }}
screen root() { var query = ""
header("Todo") topButton("Add", onclick={ addTask(); }) searchBox(query) group { list(t in Task.search(query)) { item { checkbox(t.done) " " label(t.name) } } }}
scripting
function cleanDoneTasks() : Num { var removed = 0; for(t in Task.all()) { if(t.done) { remove(t); removed = removed + 1; } } return removed;}
data binding
var n = 0
label(n)
button("Up", onclick={ n = n + 1;})
one-way
var n = 0
inputNum(n)label(n)
two-way
reactive/dataflow programming
var amount = 10var percentage = 10var total <- amount * (1 + percentage/100)
inputNum(amount)inputNum(percentage)label(total)
web services
{ "total": 746646, "page": 1, "pagesize": 30, "questions": [ { "tags": ["string", "assembly", "arm"], "answers": [], "question_id": 3092029, "owner": { "user_id": 320124, "user_type": "registered", "display_name": "SoulBeaver", "reputation": 195 }, "creation_date": 1277200629, "score": 0, "title": "ARM - Infinite Loop While Searching String", "body": "...", ... }, ... ]}
http://api.stackoverflow.com/0.8/questions?answers=true&body=true
{ "total": 746646, "page": 1, "pagesize": 30, "questions": [ { "tags": ["string", "assembly", "arm"], "answers": [], "question_id": 3092029, "owner": { "user_id": 320124, "user_type": "registered", "display_name": "SoulBeaver", "reputation": 195 }, "creation_date": 1277200629, "score": 0, "title": "ARM - Infinite Loop While Searching String", "body": "...", ... }, ... ]}
external type QuestionsResultSet { total : Num page : Num pagesize : Num questions : Array<QuestionResult>}
{ "tags": ["string", "assembly", "arm"], "answers": [], "question_id": 3092029, "owner": { "user_id": 320124, "user_type": "registered", "display_name": "SoulBeaver", "reputation": 195 }, "creation_date": 1277200629, "score": 0, "title": "ARM - Infinite Loop While Searching String", "body": "...", ...}
external type QuestionResult { tags : Array<String> answers : Array<AnswerResult> owner : OwnerResult creation_date : Num ...}
service StackOverflow { root = "http://api.stackoverflow.com/0.8" resource questions(answers : Bool, body : Bool) : QuestionsResultSet { uri = "/questions" method = "GET" encoding = "json" } ...}
function fetchQuestions() { var res = StackOverflow.questions(answers=true, body=true); for(question : QuestionResult in res.questions) { mapQuestion(question); }}
entity Question { questionId : Num title : String body : Text answers : Collection<Answer> (inverse: question) creationDate : DateTime owner : User}
entity Answer { question : Question answerId : Num owner : User body : Text}
entity User { userId : Num name : String reputation : Num}
function mapQuestion(qr : QuestionResult) : Question { var q : Question = cachedQuestion(remote.question_id); if(q == null) { q = Question { questionId = qr.question_id, title = qr.title, body = qr.body, answers = mapAnswers(qr.answers), creationDate = DateTime.fromTimestamp(qr.creation_date), owner = mapUser(qr.owner) }; add(q); } return q;}
implementation
parse
check
desugar
generate code
mobl code
HTML/Javascript
entity Task { name : String (searchable) done : Bool dueDate : DateTime}
tasks.Task = persistence.define('tasks__Task', { 'name': 'TEXT', 'done': 'BOOL', 'dueDate': 'DATE'});tasks.Task.textIndex('name');
Javascript usingpersistence.js
HTML5 ORM
screen root() { header("Todo") ...}
tasks.root = function(callback, screenCallback) { var root1018 = $("<div>"); mobl.header(ref("Todo"), function(node) { root1018.append(node); ... });};
Javascript functions building DOM
function cleanDoneTasks() : Num { var removed = 0; for(t in Task.all()) { if(t.done) { remove(t); removed = removed + 1; } } return removed;}
tasks.cleanDoneTasks = function() { var removed = 0; var results = Task.all(); for(var i = 0; i < results.length; i++) { var t = results[i]; if(t.done) { remove(t); removed = removed + 1; } } return removed;}
function cleanDoneTasks() : Num { var removed = 0; for(t in Task.all()) { if(t.done) { remove(t); removed = removed + 1; } } return removed;}
tasks.cleanDoneTasks = function() { var removed = 0; var results = Task.all(); for(var i = 0; i < results.length; i++) { var t = results[i]; if(t.done) { remove(t); removed = removed + 1; } } return removed;};
tasks.cleanDoneTasks = function(callback) { var removed = 0; Task.all(function(results) { for(var i = 0; i < results.length; i++) { var t = results[i]; if(t.done) { remove(t); removed = removed + 1; } } callback(removed); });};
tasks.cleanDoneTasks = function() { var removed = 0; var results = Task.all(); for(var i = 0; i < results.length; i++) { var t = results[i]; if(t.done) { remove(t); removed = removed + 1; } } return removed;};
continuation-passing style transform
reactive programming
screen root() { var n = 8 label(n * n) button("Inc", onclick={ n = n + 1; })}
var n = 8
var n = ref(8);
Observable- set(value)- get()- addEventListener(eventType, callback)
label(n * n)
var node565 = $("<span>");node565.text(n.get() * n.get());n.addEventListener("change", function() { node565.text(n.get() * n.get());});root.append(node565);
button("Inc", onclick={ n = n + 1;})
var nodes566 = $("<span class='button'>");node566.text("Inc");node566.click(function() { n.set(n.get() + 1);});root.append(node566);
screen root() { var n = 8 label(n * n) button("Inc", onclick={ n = n + 1; })}
conclusion
many mobile platforms
HTML5/JS
avoid AppStore approval
statically-typed WebDSL-like language
generates HTML/JS
CPS transform/reactive programming
get it?
http://mobl-lang.org
http://spoofax.org