mobl

Preview:

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

Recommended