mobl: Een DSL voor mobiele applicatieontwikkeling

Post on 24-Jun-2015

357 views 1 download

Tags:

transcript

Zef Hemel, Eelco Visser

230,000/day

200,000/day

applicationdevelopment

Objective-C

Objective-C Java

Objective-C Java J2ME/C++

Objective-C Java J2ME/C++

HTML/Javascript

Objective-C Java J2ME/C++

HTML/Javascript Java

Objective-C Java J2ME/C++

HTML/Javascript Java .NET

AppStore

portability

deployment

WWW

HTML

WebDatabases

WebDatabases

WebDatabases

Location information (GPS)

WebDatabases

WebDatabases

Location information (GPS)

Threading

WebDatabases

WebDatabases

Location information (GPS)

Threading

Canvas

WebDatabases

WebDatabases

Location information (GPS)

Threading

Canvas

WebDatabases

Multi-touch

WebDatabases

Location information (GPS)

Threading

Canvas

WebDatabases

Multi-touch

Offline support

WebDatabases

Location information (GPS)

Threading

Canvas

WebDatabases

Multi-touch

Offline support

Full-screen support

mobile web apps

HTML

CSS

Javascript

SQLcache

manifests

HTML

CSS

Javascript

SQLcache

manifests

user interface abstraction

user interface abstraction

var results = tx.executeQuery("SELECT * FROM Task");for(var i = 0; i < results.length; i++) { alert(results[i].name);}

synchronous programming

render page

query database and process

results

...

time

render page

query database and process

results

...

timebrowser freeze

render page

send query

...

process query result

...

time

tx.executeQuery("SELECT * FROM Task", function(results) { for(var i = 0; i < results.length; i++) { alert(results[i].name); } });...

asynchronous programming

tx.executeQuery("SELECT * FROM Task", function(results) { alert("Hello, "); });alert("world!");

tx.executeQuery("SELECT * FROM Task", function(results) { tx.executeQuery("INSERT ...", function() { alert("Selected, then inserted"); }); });

mix of loosely-coupled DSLs

poor tool-support for mixed-language projects

lack of abstraction in UI

asynchronous programming

demo

http://confplan.zef.me

task 1:user interface

http://mobl-lang.org/get.htmlhttp://webdsl.org/zip/devnology/eclipselinux64.ziphttp://webdsl.org/zip/devnology/eclipselinux.ziphttp://webdsl.org/zip/devnology/eclipsemac64.ziphttp://webdsl.org/zip/devnology/eclipsewin.zip

application tipcalculator

import mobl::ui::generic

screen root() { var amount = 10 var percentage = 10

header("Tip calculator") group { item { numField(amount, label="amount") } item { numField(percentage, label="percentage") } item { "$" label(amount * (1 + percentage/100)) } }}

application tipcalculator

import mobl::ui::generic

screen root() { var amount = 10 var percentage = 10

header("Tip calculator") group { item { numField(amount, label="amount") } item { numField(percentage, label="percentage") } item { "$" label(amount * (1 + percentage/100)) } }}

application tipcalculator

import mobl::ui::generic

screen root() { var amount = 10 var percentage = 10

header("Tip calculator") group { item { numField(amount, label="amount") } item { numField(percentage, label="percentage") } item { "$" label(amount * (1 + percentage/100)) } }}

application tipcalculator

import mobl::ui::generic

screen root() { var amount = 10 var percentage = 10

header("Tip calculator") group { item { numField(amount, label="amount") } item { numField(percentage, label="percentage") } item { "$" label(amount * (1 + percentage/100)) } }}

application tipcalculator

import mobl::ui::generic

screen root() { var amount = 10 var percentage = 10

header("Tip calculator") group { item { numField(amount, label="amount") } item { numField(percentage, label="percentage") } item { "$" label(amount * (1 + percentage/100)) } }}

application tipcalculator

import mobl::ui::generic

screen root() { var amount = 10 var percentage = 10

header("Tip calculator") group { item { numField(amount, label="amount") } item { numField(percentage, label="percentage") } item { "$" label(amount * (1 + percentage/100)) } }}

task 2:scripting

application test

import mobl::ui::generic

function fac(n : Num) : Num { if(n == 1) { return 1; } else { return n * fac(n-1); }}

screen promptNum(question : String) : Num { var answer = 0 header(question) { button("Done", onclick={ screen return answer; }) } group { item { numField(answer) } }}

screen root() { header("Calculator") var n = 2 group { item { numField(n) } } button("*", onclick={ n = n * promptNum("Multiply with"); }) button("+", onclick={ n = n + promptNum("Add"); }) button("!", onclick={ n = fac(n); })}

application test

import mobl::ui::generic

function fac(n : Num) : Num { if(n == 1) { return 1; } else { return n * fac(n-1); }}

screen promptNum(question : String) : Num { var answer = 0 header(question) { button("Done", onclick={ screen return answer; }) } group { item { numField(answer) } }}

screen root() { header("Calculator") var n = 2 group { item { numField(n) } } button("*", onclick={ n = n * promptNum("Multiply with"); }) button("+", onclick={ n = n + promptNum("Add"); }) button("!", onclick={ n = fac(n); })}

application test

import mobl::ui::generic

function fac(n : Num) : Num { if(n == 1) { return 1; } else { return n * fac(n-1); }}

screen promptNum(question : String) : Num { var answer = 0 header(question) { button("Done", onclick={ screen return answer; }) } group { item { numField(answer) } }}

screen root() { header("Calculator") var n = 2 group { item { numField(n) } } button("*", onclick={ n = n * promptNum("Multiply with"); }) button("+", onclick={ n = n + promptNum("Add"); }) button("!", onclick={ n = fac(n); })}

application todo

import mobl::ui::generic

function fac(n : Num) : Num { if(n == 1) { return 1; } else { return n * fac(n-1); }}

screen promptNum(question : String) : Num { var answer = 0 header(question) { button("Done", onclick={ screen return answer; }) } group { item { numField(answer) } }}

screen root() { header("Calculator") var n = 2 group { item { numField(n) } } button("*", onclick={ n = n * promptNum("Multiply with"); }) button("+", onclick={ n = n + promptNum("Add"); }) button("!", onclick={ n = fac(n); })}

application test

import mobl::ui::generic

function fac(n : Num) : Num { if(n == 1) { return 1; } else { return n * fac(n-1); }}

screen promptNum(question : String) : Num { var answer = 0 header(question) { button("Done", onclick={ screen return answer; }) } group { item { numField(answer) } }}

screen root() { header("Calculator") var n = 2 group { item { numField(n) } } button("*", onclick={ n = n * promptNum("Multiply with"); }) button("+", onclick={ n = n + promptNum("Add"); }) button("!", onclick={ n = fac(n); })}

var pos = getPosition();alert("Your location is: " + pos);

var pos = getPosition();alert("Your location is: " + pos);

var pos;getPosition(function(result) { pos = result; alert("Your location is: " + pos); ...});

continuation-passingstyle transformation

task 3:data modeling and query

entity Task { name : String (searchable) done : Bool dateAdded : DateTime}

entity Task { name : String (searchable) done : Bool dateAdded : DateTime category : Category (inverse: tasks) tags : Collection<Tag> (inverse: tasks)}

entity Category { name : String tasks : Collection<Task> (inverse: category)}

entity Tag { name : String tasks : Collection<Task> (inverse: tags)}

screen root() { header("Tasks") { button("Add", onclick={ addTask(); }) } group { list(t in Task.all() order by dateAdded desc) { taskItem(t) } } button("Search", onclick={ search(); })}

screen search() { var phrase = "" header("Search") { backButton() } searchBox(phrase) group { list(t in Task.searchPrefix(phrase)) { taskItem(t) } }}

screen root() { header("Tasks") { button("Add", onclick={ addTask(); }) } group { list(t in Task.all() order by dateAdded desc) { taskItem(t) } } button("Search", onclick={ search(); })}

screen search() { var phrase = "" header("Search") { backButton() } searchBox(phrase) group { list(t in Task.searchPrefix(phrase)) { taskItem(t) } }}

control taskItem(t : Task) { item(onclick={ taskDetails(t); }) { checkBox(t.done) " " label(t.name) contextMenu { button("Remove", onclick={ remove(t); }) } }}

control taskItem(t : Task) { item(onclick={ taskDetails(t); }) { checkBox(t.done) " " label(t.name) contextMenu { button("Remove", onclick={ remove(t); }) } }}

Task.all()

Task.all() where done == false && ...order by dateAdded desclimit 10offset 5

Task.all() where done == false && ...order by dateAdded desclimit 10offset 5

task 4:higher-order controls and

native interfaces

control taskItem(t : Task) { checkBox(t.done) " " label(t.name) contextMenu { button("Remove", onclick={ remove(t); }) }}

control taskDetail(t : Task) { group { item { textField(t.name, placeholder="Task name") } item { checkBox(t.done, label="Done") } }}

screen root() { header("Tasks") { button("Add", onclick={ addTask(); }) } masterDetail(Task.all() order by dateAdded desc, taskItem, taskDetail) button("Search", onclick={ search(); })}

control taskItem(t : Task) { checkBox(t.done) " " label(t.name) contextMenu { button("Remove", onclick={ remove(t); }) }}

control taskDetail(t : Task) { group { item { textField(t.name, placeholder="Task name") } item { checkBox(t.done, label="Done") } }}

screen root() { header("Tasks") { button("Add", onclick={ addTask(); }) } masterDetail(Task.all() order by dateAdded desc, taskItem, taskDetail) button("Search", onclick={ search(); })}

control masterDetail(items : Collection<Dynamic>, masterItem : Control1<Dynamic>, detail : Control1<Dynamic>) { group { list(it in items) { item(onclick={ detailScreen(it, detail); }) { masterItem(it) } } }}

screen detailScreen(it : Dynamic, detail : Control1<Dynamic>) { header("Detail") { backButton("Back", onclick={ screen return; }) } detail(it)}

external type LocalStorage { static sync function getItem(key : String) : Dynamic static sync function setItem(key : String, val : Object) : void static sync function removeItem(key : String) : void static sync function clear() : void}

<javascript>firstapp.LocalStorage = window.localStorage;</javascript>

what else is there?

service DataProvider { resource getNearbyConferences(lat : Num, long : Num) : [JSON] { uri = "/nearbyConferences" } resource fetchProgram(conferenceId : String) : [JSON] { uri = "/conferenceProgram" }}

web services

service DataProvider { resource getNearbyConferences(lat : Num, long : Num) : [JSON] { uri = "/nearbyConferences" } resource fetchProgram(conferenceId : String) : [JSON] { uri = "/conferenceProgram" }}

function loadLocalConferences() { var position = getPosition(); var nearbyConferencesJson = DataProvider.getNearbyConferences(position.latitude, position.longitude); foreach(jsonObj in nearbyConferencesJson) { Conference.fromSelectJson(jsonObj); }}

web services

location & maps

mobl::ui::ios mobl::ui::jq

mobl::ui::ios mobl::ui::jq

mobl::ui

application confplan

offline truetitle "ConfPlan"

future

adaptive user interfaces

database sync

more high-level controls

web/native hybrid (PhoneGap)