Software Language Design & Engineering Eelco Visser http://eelcovisser.org Zürich, May 12, 2011
Software Engineering Problem Machine Domain bridging the gap between problem domain and solution domain
High-Level Languages Problem HLL Machine Domain HLLs reduce gap
Domain-Specific Languages Problem DSL HLL Machine Domain domain-specific languages support more specialization
Domain-Specific Languages Even Higher- Level Languages Problem DSL HLL Machine Domain domain-specific languages support more specialization
Software Language Design & Engineering enable software engineers to effectively design, implement, and apply domain-specific software languages
Research: Software Language Design Systematically design domain- specific software languages with optimal tradeoff between expressivity, completeness, portability, coverage, and maintainability
Expressivity vs Coverage expressivity: fewer irrelevant details => conciseness coverage: which portion of domain can we express
Software Language Design Case Studies Mobl: client-side stateful web applications WebDSL: server-side restful web applications
http://www.mobl-lang.org Zef Hemel
Divergence in Mobile Platforms Objective-C Java J2ME/C++ HTML/Javascript Java .NET Native Applications not Portable
Convergence in Mobile Platform Webkit browser Webkit browser Webkit browser Webkit browser
The Universal Userinterface Engine
Mobile Web Architecture
Rich Applications Audio WebDatabases Full-screen support Location information (GPS) Offline support Canvas Accelerator support Multi-touch
Native Applications Address book Camera Compass File IO Notifications
Software Engineering with JavaScript annotated HTML imperative Javascript MVC, No Integration, No Abstraction, Accidental Complexity
typed declarative integrated concise
Web Application with Touch
Portable Applications
Mobl Architecture
tipcalculator.mobl application tipcalculator import mobl::ui::generic screen root() { var amount = 20 var percentage = 10 header("Tip calculator") group { item { numField(amount, label="amount") } item { numField(percentage, label="percentage") } item { "$" label(Math.round(amount * (1 + percentage/100))) } } nl() }
Model-View Pattern
Task Manager
Data Model entity Task { name : String (searchable) done : Bool due : 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) } HTML5 Data Persistence
Logic entity Task { name : String (searchable) done : Bool due : DateTime category : Category (inverse: tasks) tags : Collection<Tag> (inverse: tasks) function postpone(days : Num) { this.due = DateTime.create( this.due.getFullYear(), this.due.getMonth(), this.due.getDate() + days); } function import(user : String, pw : String) { var tasksJSON = httpRequest("/export?user="+ user + "&pw=" + pw); foreach(t in tasksJSON) { add(Task.fromSelectJSON(t)); } } } statically typed: catch errors early
Reactive User Interfaces screen root() { var phrase = "" header("Tasks") { button("Add", onclick={ addTask(); }) } searchBox(phrase) group { list(t in Task.search(phrase) limit 20){ item { checkBox(t.done, label=t.name) } } } }
Reactive User Interfaces screen root() { var phrase = "" header("Tasks") { button("Add", onclick={ addTask(); }) } searchBox(phrase) group { list(t in Task.search(phrase) limit 20){ item { checkBox(t.done, label=t.name) } } } }
Navigation screen root() { var phrase = "" header("Tasks") { button("Add", onclick={ addTask(); }) } searchBox(phrase) group { list(t in Task.search(phrase) limit 20){ item { checkBox(t.done, label=t.name) } } } }
Navigation screen root() { { addTask() var phrase = "" t = Task() header("Add") { header("Tasks") { button("Done", onclick={ button("Add", onclick={ addTask(); }) } add(t); searchBox(phrase) screen return; group { }) } list(t in Task.search(phrase) limit 20){ textField(t.name) item { datePicker(t.due) checkBox(t.done, label=t.name) } } } } }
Navigation screen root() { var phrase = "" header("Tasks") { button("Add", onclick={ addTask(); }) } searchBox(phrase) group { list(t in Task.search(phrase) limit 20){ item { checkBox(t.done, label=t.name) } } } }
Continuations screen root() { button("Ask", onclick={ alert("Hello " + prompt("First name") + " " + prompt("Last name")); }) } screen prompt(question : String) : String { var answer = "" header(question) { button("Done", onclick={ screen return answer; }) } textField(answer) }
User Interface Idiom: Tab control tab1() { header("Tab 1") label("This is tab 1") } control tab2() { header("Tab 2") label("This is tab 2") } screen root() { tabSet([("One", tab1), ("Two", tab2)], defaultTab="One") }
Tab Set: Higher-Order Control control tabSet(tabs : [(String,Control)], activeTab : String) { list((tabName, tabControl) in tabs) { block(onclick={ activeTab = tabName; }, style=activeTab==tabName ? activeTabButton : inactiveTabButton) { label(tabName) } } list((tabName, tabControl) in tabs) { block(activeTab==tabName ? visibleTab : invisibleTab) { tabControl() } } } increase coverage: developers can create abstractions
User Interface Idiom: Master Detail control taskItem(t : Task) { checkBox(t.done, label=t.name) } control taskDetail(t : Task) { textField(t.name) datePicker(t.due) } screen root() { header("Tasks") masterDetail(Task.all() order by due desc, taskItem, taskDetail) }
User Interface Idiom: Master Detail control taskItem(t : Task) { checkBox(t.done, label=t.name) } control taskDetail(t : Task) { textField(t.name) datePicker(t.due) } screen root() { header("Tasks") masterDetail(Task.all() order by due desc, taskItem, taskDetail) }
Master Detail: Higher-Order Control control masterDetail(items : Collection<?>, masterItem : Control1<?>, detail : Control1<?>) { group { list(it in items) { item(onclick={ detailScreen(it,detail); }) { masterItem(it) } } } } screen detailScreen(it : ?, detail : Control1<?>) { header("Detail") { backButton() } detail(it) }
Mobl Applications
GR8 Conference Program
mPodder
Ken http://itsneh.com/ken/
Mobl IDE static cross-concern consistency checking
Research: Software Language Engineering Automatically derive efficient, scalable, incremental compiler + usable IDE from high-level, declarativelanguage definition
The Spoofax Language Workbench creating full featured IDEs for domain-specific languages
The Spoofax Language Workbench an IDE for developing languages and their IDEs
The Spoofax Language Workbench Integrated Language Definition Testing
Syntax Definition module MoBL-Data imports Common MoBL exports context-free syntax MetaAnno* "entity" QId ":" Type "{" Property* "}" -> Definition {cons("Entity")} MetaAnno* "entity" QId "{" Property* "}" -> Definition {cons("EntityNoSuper")} MetaAnno* ID ":" Type "(" {Anno ","}* ")" -> Property {cons("Property")} MetaAnno* ID ":" Type -> Property {cons("PropertyNoAnnos")} ID -> Anno {cons("SimpleAnno")} "inverse" ":" ID -> Anno {cons("InverseAnno")} context-free syntax "@sync" UriPath -> MetaAnno {cons("SyncEntityAnno")}
Type Checking rules constraint-error : t@SimpleType(_) -> (t, $[Type is not defined: [<pp-mobl-type> t]]) where not(<lookup-type> t) constraint-error : t@FieldAccess(e, x) -> (t, $[Property [x] not defined]) where <type-of> e where not(type-of)
Code Generation rules property-to-js(|ent) : Property(_, x, t@SimpleType(_), anno*) -> $['[x]': '[sqlType]'] where not(<is-entity-type> t) with sqlType := <sql-type> t ; if [_] := <filter(?SimpleAnno("searchable"))> anno* then rules ( SearchableProperties :+= (ent, x) ) end
Software Language Design & Engineering http://eelcovisser.org Language Design as Engineering Discipline http://spoofax.org (More) Declarative Language Definition http://mobl-lang.org Static Analysis of Language Definitions http://webdsl.org Language Workbench in the Cloud http://researchr.org http://researchr.org/search/publication/mobl+spoofax+webdsl

Software Language Design & Engineering

  • 1.
    Software Language Design &Engineering Eelco Visser http://eelcovisser.org Zürich, May 12, 2011
  • 2.
    Software Engineering Problem Machine Domain bridging the gap between problem domain and solution domain
  • 3.
    High-Level Languages Problem HLL Machine Domain HLLs reduce gap
  • 4.
    Domain-Specific Languages Problem DSL HLL Machine Domain domain-specific languages support more specialization
  • 5.
    Domain-Specific Languages Even Higher- Level Languages Problem DSL HLL Machine Domain domain-specific languages support more specialization
  • 6.
    Software Language Design& Engineering enable software engineers to effectively design, implement, and apply domain-specific software languages
  • 7.
    Research: Software LanguageDesign Systematically design domain- specific software languages with optimal tradeoff between expressivity, completeness, portability, coverage, and maintainability
  • 8.
    Expressivity vs Coverage expressivity:fewer irrelevant details => conciseness coverage: which portion of domain can we express
  • 9.
    Software Language DesignCase Studies Mobl: client-side stateful web applications WebDSL: server-side restful web applications
  • 10.
  • 11.
    Divergence in MobilePlatforms Objective-C Java J2ME/C++ HTML/Javascript Java .NET Native Applications not Portable
  • 12.
    Convergence in MobilePlatform Webkit browser Webkit browser Webkit browser Webkit browser
  • 13.
  • 14.
  • 15.
    Rich Applications Audio WebDatabases Full-screen support Location information (GPS) Offline support Canvas Accelerator support Multi-touch
  • 16.
    Native Applications Address book Camera Compass File IO Notifications
  • 17.
    Software Engineering withJavaScript annotated HTML imperative Javascript MVC, No Integration, No Abstraction, Accidental Complexity
  • 18.
    typed declarative integrated concise
  • 19.
  • 20.
  • 21.
  • 22.
    tipcalculator.mobl application tipcalculator import mobl::ui::generic screenroot() { var amount = 20 var percentage = 10 header("Tip calculator") group { item { numField(amount, label="amount") } item { numField(percentage, label="percentage") } item { "$" label(Math.round(amount * (1 + percentage/100))) } } nl() }
  • 23.
  • 24.
  • 25.
    Data Model entity Task{ name : String (searchable) done : Bool due : 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) } HTML5 Data Persistence
  • 26.
    Logic entity Task { name : String (searchable) done : Bool due : DateTime category : Category (inverse: tasks) tags : Collection<Tag> (inverse: tasks) function postpone(days : Num) { this.due = DateTime.create( this.due.getFullYear(), this.due.getMonth(), this.due.getDate() + days); } function import(user : String, pw : String) { var tasksJSON = httpRequest("/export?user="+ user + "&pw=" + pw); foreach(t in tasksJSON) { add(Task.fromSelectJSON(t)); } } } statically typed: catch errors early
  • 27.
    Reactive User Interfaces screenroot() { var phrase = "" header("Tasks") { button("Add", onclick={ addTask(); }) } searchBox(phrase) group { list(t in Task.search(phrase) limit 20){ item { checkBox(t.done, label=t.name) } } } }
  • 28.
    Reactive User Interfaces screenroot() { var phrase = "" header("Tasks") { button("Add", onclick={ addTask(); }) } searchBox(phrase) group { list(t in Task.search(phrase) limit 20){ item { checkBox(t.done, label=t.name) } } } }
  • 29.
    Navigation screen root() { var phrase = "" header("Tasks") { button("Add", onclick={ addTask(); }) } searchBox(phrase) group { list(t in Task.search(phrase) limit 20){ item { checkBox(t.done, label=t.name) } } } }
  • 30.
    Navigation screen root() {{ addTask() var phrase = "" t = Task() header("Add") { header("Tasks") { button("Done", onclick={ button("Add", onclick={ addTask(); }) } add(t); searchBox(phrase) screen return; group { }) } list(t in Task.search(phrase) limit 20){ textField(t.name) item { datePicker(t.due) checkBox(t.done, label=t.name) } } } } }
  • 31.
    Navigation screen root() { var phrase = "" header("Tasks") { button("Add", onclick={ addTask(); }) } searchBox(phrase) group { list(t in Task.search(phrase) limit 20){ item { checkBox(t.done, label=t.name) } } } }
  • 32.
    Continuations screen root() { button("Ask", onclick={ alert("Hello " + prompt("First name") + " " + prompt("Last name")); }) } screen prompt(question : String) : String { var answer = "" header(question) { button("Done", onclick={ screen return answer; }) } textField(answer) }
  • 33.
    User Interface Idiom:Tab control tab1() { header("Tab 1") label("This is tab 1") } control tab2() { header("Tab 2") label("This is tab 2") } screen root() { tabSet([("One", tab1), ("Two", tab2)], defaultTab="One") }
  • 34.
    Tab Set: Higher-OrderControl control tabSet(tabs : [(String,Control)], activeTab : String) { list((tabName, tabControl) in tabs) { block(onclick={ activeTab = tabName; }, style=activeTab==tabName ? activeTabButton : inactiveTabButton) { label(tabName) } } list((tabName, tabControl) in tabs) { block(activeTab==tabName ? visibleTab : invisibleTab) { tabControl() } } } increase coverage: developers can create abstractions
  • 35.
    User Interface Idiom:Master Detail control taskItem(t : Task) { checkBox(t.done, label=t.name) } control taskDetail(t : Task) { textField(t.name) datePicker(t.due) } screen root() { header("Tasks") masterDetail(Task.all() order by due desc, taskItem, taskDetail) }
  • 36.
    User Interface Idiom:Master Detail control taskItem(t : Task) { checkBox(t.done, label=t.name) } control taskDetail(t : Task) { textField(t.name) datePicker(t.due) } screen root() { header("Tasks") masterDetail(Task.all() order by due desc, taskItem, taskDetail) }
  • 37.
    Master Detail: Higher-OrderControl control masterDetail(items : Collection<?>, masterItem : Control1<?>, detail : Control1<?>) { group { list(it in items) { item(onclick={ detailScreen(it,detail); }) { masterItem(it) } } } } screen detailScreen(it : ?, detail : Control1<?>) { header("Detail") { backButton() } detail(it) }
  • 38.
  • 39.
  • 40.
  • 41.
  • 42.
    Mobl IDE static cross-concernconsistency checking
  • 43.
    Research: Software LanguageEngineering Automatically derive efficient, scalable, incremental compiler + usable IDE from high-level, declarativelanguage definition
  • 44.
    The Spoofax LanguageWorkbench creating full featured IDEs for domain-specific languages
  • 45.
    The Spoofax LanguageWorkbench an IDE for developing languages and their IDEs
  • 46.
    The Spoofax LanguageWorkbench Integrated Language Definition Testing
  • 47.
    Syntax Definition module MoBL-Data imports Common MoBL exports context-free syntax MetaAnno* "entity" QId ":" Type "{" Property* "}" -> Definition {cons("Entity")} MetaAnno* "entity" QId "{" Property* "}" -> Definition {cons("EntityNoSuper")} MetaAnno* ID ":" Type "(" {Anno ","}* ")" -> Property {cons("Property")} MetaAnno* ID ":" Type -> Property {cons("PropertyNoAnnos")} ID -> Anno {cons("SimpleAnno")} "inverse" ":" ID -> Anno {cons("InverseAnno")} context-free syntax "@sync" UriPath -> MetaAnno {cons("SyncEntityAnno")}
  • 48.
    Type Checking rules constraint-error : t@SimpleType(_) -> (t, $[Type is not defined: [<pp-mobl-type> t]]) where not(<lookup-type> t) constraint-error : t@FieldAccess(e, x) -> (t, $[Property [x] not defined]) where <type-of> e where not(type-of)
  • 49.
    Code Generation rules property-to-js(|ent) : Property(_, x, t@SimpleType(_), anno*) -> $['[x]': '[sqlType]'] where not(<is-entity-type> t) with sqlType := <sql-type> t ; if [_] := <filter(?SimpleAnno("searchable"))> anno* then rules ( SearchableProperties :+= (ent, x) ) end
  • 50.
    Software Language Design& Engineering http://eelcovisser.org Language Design as Engineering Discipline http://spoofax.org (More) Declarative Language Definition http://mobl-lang.org Static Analysis of Language Definitions http://webdsl.org Language Workbench in the Cloud http://researchr.org http://researchr.org/search/publication/mobl+spoofax+webdsl