Arrows in Commercial Web Applications - HotWeb2016.pdf · Research History Directing JavaScript...
Transcript of Arrows in Commercial Web Applications - HotWeb2016.pdf · Research History Directing JavaScript...
Arrows in Commercial Web Applications
Eric Fritz Jose Antony Tian Zhao
University of Wisconsin - Milwaukee
1 / 13
Research History
Directing JavaScript with ArrowsKhoo Yit Phang, Michael Hicks, Jeffrey S. Foster, and Vibha Sazawal.5th Symposium on Dynamic Languages, 2009.
Inferring Types of Asynchronous Arrows in JavaScriptEric Fritz and Tian Zhao.Reactive and Event-based Languages and Systems, 2015.
Typing and Semantics of Asynchronous Arrows in JavaScriptEric Fritz and Tian Zhao.In Review, 2016.
Arrows in Commercial Web ApplicationsEric Fritz, Jose Antony, and Tian Zhao.HotWeb, 2016.
2 / 13
Research History
Directing JavaScript with ArrowsKhoo Yit Phang, Michael Hicks, Jeffrey S. Foster, and Vibha Sazawal.5th Symposium on Dynamic Languages, 2009.
Inferring Types of Asynchronous Arrows in JavaScriptEric Fritz and Tian Zhao.Reactive and Event-based Languages and Systems, 2015.
Typing and Semantics of Asynchronous Arrows in JavaScriptEric Fritz and Tian Zhao.In Review, 2016.
Arrows in Commercial Web ApplicationsEric Fritz, Jose Antony, and Tian Zhao.HotWeb, 2016.
2 / 13
Research History
Directing JavaScript with ArrowsKhoo Yit Phang, Michael Hicks, Jeffrey S. Foster, and Vibha Sazawal.5th Symposium on Dynamic Languages, 2009.
Inferring Types of Asynchronous Arrows in JavaScriptEric Fritz and Tian Zhao.Reactive and Event-based Languages and Systems, 2015.
Typing and Semantics of Asynchronous Arrows in JavaScriptEric Fritz and Tian Zhao.In Review, 2016.
Arrows in Commercial Web ApplicationsEric Fritz, Jose Antony, and Tian Zhao.HotWeb, 2016.
2 / 13
Research History
Directing JavaScript with ArrowsKhoo Yit Phang, Michael Hicks, Jeffrey S. Foster, and Vibha Sazawal.5th Symposium on Dynamic Languages, 2009.
Inferring Types of Asynchronous Arrows in JavaScriptEric Fritz and Tian Zhao.Reactive and Event-based Languages and Systems, 2015.
Typing and Semantics of Asynchronous Arrows in JavaScriptEric Fritz and Tian Zhao.In Review, 2016.
Arrows in Commercial Web ApplicationsEric Fritz, Jose Antony, and Tian Zhao.HotWeb, 2016.
2 / 13
Example Application - Inventory Search
Requirements:Filter displayed results by name and category
Paginate results when filtered set is > 50 items
Performance Optimizations:Cache server results for same query/page
Attempt to pre-fetch the next page of resultsDon’t make a remote request while the user is typing
3 / 13
Example Application - Inventory Search
Requirements:Filter displayed results by name and category
Paginate results when filtered set is > 50 items
Performance Optimizations:Cache server results for same query/page
Attempt to pre-fetch the next page of resultsDon’t make a remote request while the user is typing
3 / 13
Example Application - Inventory Search
Requirements:Filter displayed results by name and category
Paginate results when filtered set is > 50 items
Performance Optimizations:Cache server results for same query/page
Attempt to pre-fetch the next page of resultsDon’t make a remote request while the user is typing
3 / 13
Sequencing Filtering + Paging (Using Callbacks)
function call(query , page , handler) {
$.ajax({ 'url': makeURL(query , page), 'success ': handler });
}
function showPage(resp) {
const pageTo = (page) => () => {
$('#prev , #next'). unbind('click ');call(resp.query , page , showPage );
};
displayTable(resp);
$('#prev').one('click ', pageTo(resp.prev ));
$('#next').one('click ', pageTo(resp.next ));
}
$('#filter ').keyup((ev) => {
$('#prev , #next'). unbind('click ');call($(ev.target ).val(), 1, showPage );
});
4 / 13
Sequencing Filtering + Paging (Using Callbacks)
function call(query , page , handler) {
$.ajax({ 'url': makeURL(query , page), 'success ': handler });
}
function showPage(resp) {
const pageTo = (page) => () => {
$('#prev , #next'). unbind('click ');call(resp.query , page , showPage );
};
displayTable(resp);
$('#prev').one('click ', pageTo(resp.prev ));
$('#next').one('click ', pageTo(resp.next ));
}
$('#filter ').keyup((ev) => {
$('#prev , #next'). unbind('click ');call($(ev.target ).val(), 1, showPage );
});
Call Stack Event Queue
main
4 / 13
Sequencing Filtering + Paging (Using Callbacks)
function call(query , page , handler) {
$.ajax({ 'url': makeURL(query , page), 'success ': handler });
}
function showPage(resp) {
const pageTo = (page) => () => {
$('#prev , #next'). unbind('click ');call(resp.query , page , showPage );
};
displayTable(resp);
$('#prev').one('click ', pageTo(resp.prev ));
$('#next').one('click ', pageTo(resp.next ));
}
$('#filter ').keyup((ev) => {
$('#prev , #next'). unbind('click ');call($(ev.target ).val(), 1, showPage );
});
Call Stack Event Queue
main keyup keyup 7→ closure1
4 / 13
Sequencing Filtering + Paging (Using Callbacks)
function call(query , page , handler) {
$.ajax({ 'url': makeURL(query , page), 'success ': handler });
}
function showPage(resp) {
const pageTo = (page) => () => {
$('#prev , #next'). unbind('click ');call(resp.query , page , showPage );
};
displayTable(resp);
$('#prev').one('click ', pageTo(resp.prev ));
$('#next').one('click ', pageTo(resp.next ));
}
$('#filter ').keyup((ev) => {
$('#prev , #next'). unbind('click ');call($(ev.target ).val(), 1, showPage );
});
Call Stack Event Queue
main keyup 7→ closure1
4 / 13
Sequencing Filtering + Paging (Using Callbacks)
function call(query , page , handler) {
$.ajax({ 'url': makeURL(query , page), 'success ': handler });
}
function showPage(resp) {
const pageTo = (page) => () => {
$('#prev , #next'). unbind('click ');call(resp.query , page , showPage );
};
displayTable(resp);
$('#prev').one('click ', pageTo(resp.prev ));
$('#next').one('click ', pageTo(resp.next ));
}
$('#filter ').keyup((ev) => {
$('#prev , #next'). unbind('click ');call($(ev.target ).val(), 1, showPage );
});
Call Stack Event Queue
keyup 7→ closure1
4 / 13
Sequencing Filtering + Paging (Using Callbacks)
function call(query , page , handler) {
$.ajax({ 'url': makeURL(query , page), 'success ': handler });
}
function showPage(resp) {
const pageTo = (page) => () => {
$('#prev , #next'). unbind('click ');call(resp.query , page , showPage );
};
displayTable(resp);
$('#prev').one('click ', pageTo(resp.prev ));
$('#next').one('click ', pageTo(resp.next ));
}
$('#filter ').keyup((ev) => {
$('#prev , #next'). unbind('click ');call($(ev.target ).val(), 1, showPage );
});
Call Stack Event Queue
closure1 keyup 7→ closure1
4 / 13
Sequencing Filtering + Paging (Using Callbacks)
function call(query , page , handler) {
$.ajax({ 'url': makeURL(query , page), 'success ': handler });
}
function showPage(resp) {
const pageTo = (page) => () => {
$('#prev , #next'). unbind('click ');call(resp.query , page , showPage );
};
displayTable(resp);
$('#prev').one('click ', pageTo(resp.prev ));
$('#next').one('click ', pageTo(resp.next ));
}
$('#filter ').keyup((ev) => {
$('#prev , #next'). unbind('click ');call($(ev.target ).val(), 1, showPage );
});
Call Stack Event Queue
callclosure1 keyup 7→ closure1 ajax 7→ showPage
4 / 13
Sequencing Filtering + Paging (Using Callbacks)
function call(query , page , handler) {
$.ajax({ 'url': makeURL(query , page), 'success ': handler });
}
function showPage(resp) {
const pageTo = (page) => () => {
$('#prev , #next'). unbind('click ');call(resp.query , page , showPage );
};
displayTable(resp);
$('#prev').one('click ', pageTo(resp.prev ));
$('#next').one('click ', pageTo(resp.next ));
}
$('#filter ').keyup((ev) => {
$('#prev , #next'). unbind('click ');call($(ev.target ).val(), 1, showPage );
});
Call Stack Event Queue
closure1 keyup 7→ closure1 ajax 7→ showPage
4 / 13
Sequencing Filtering + Paging (Using Callbacks)
function call(query , page , handler) {
$.ajax({ 'url': makeURL(query , page), 'success ': handler });
}
function showPage(resp) {
const pageTo = (page) => () => {
$('#prev , #next'). unbind('click ');call(resp.query , page , showPage );
};
displayTable(resp);
$('#prev').one('click ', pageTo(resp.prev ));
$('#next').one('click ', pageTo(resp.next ));
}
$('#filter ').keyup((ev) => {
$('#prev , #next'). unbind('click ');call($(ev.target ).val(), 1, showPage );
});
Call Stack Event Queue
keyup 7→ closure1 ajax 7→ showPage
4 / 13
Sequencing Filtering + Paging (Using Callbacks)
function call(query , page , handler) {
$.ajax({ 'url': makeURL(query , page), 'success ': handler });
}
function showPage(resp) {
const pageTo = (page) => () => {
$('#prev , #next'). unbind('click ');call(resp.query , page , showPage );
};
displayTable(resp);
$('#prev').one('click ', pageTo(resp.prev ));
$('#next').one('click ', pageTo(resp.next ));
}
$('#filter ').keyup((ev) => {
$('#prev , #next'). unbind('click ');call($(ev.target ).val(), 1, showPage );
});
Call Stack Event Queue
showPage keyup 7→ closure1 ajax 7→ showPage
4 / 13
Sequencing Filtering + Paging (Using Callbacks)
function call(query , page , handler) {
$.ajax({ 'url': makeURL(query , page), 'success ': handler });
}
function showPage(resp) {
const pageTo = (page) => () => {
$('#prev , #next'). unbind('click ');call(resp.query , page , showPage );
};
displayTable(resp);
$('#prev').one('click ', pageTo(resp.prev ));
$('#next').one('click ', pageTo(resp.next ));
}
$('#filter ').keyup((ev) => {
$('#prev , #next'). unbind('click ');call($(ev.target ).val(), 1, showPage );
});
Call Stack Event Queue
showPage keyup 7→ closure1 prev 7→ closure2next 7→ closure3
4 / 13
Sequencing Filtering + Paging (Using Callbacks)
function call(query , page , handler) {
$.ajax({ 'url': makeURL(query , page), 'success ': handler });
}
function showPage(resp) {
const pageTo = (page) => () => {
$('#prev , #next'). unbind('click ');call(resp.query , page , showPage );
};
displayTable(resp);
$('#prev').one('click ', pageTo(resp.prev ));
$('#next').one('click ', pageTo(resp.next ));
}
$('#filter ').keyup((ev) => {
$('#prev , #next'). unbind('click ');call($(ev.target ).val(), 1, showPage );
});
Call Stack Event Queue
keyup 7→ closure1 prev 7→ closure2next 7→ closure3
4 / 13
Sequencing Filtering + Paging (Using Callbacks)
function call(query , page , handler) {
$.ajax({ 'url': makeURL(query , page), 'success ': handler });
}
function showPage(resp) {
const pageTo = (page) => () => {
$('#prev , #next'). unbind('click ');call(resp.query , page , showPage );
};
displayTable(resp);
$('#prev').one('click ', pageTo(resp.prev ));
$('#next').one('click ', pageTo(resp.next ));
}
$('#filter ').keyup((ev) => {
$('#prev , #next'). unbind('click ');call($(ev.target ).val(), 1, showPage );
});
Call Stack Event Queue
closure3 keyup 7→ closure1 prev 7→ closure2next 7→ closure3
4 / 13
Sequencing Filtering + Paging (Using Callbacks)
function call(query , page , handler) {
$.ajax({ 'url': makeURL(query , page), 'success ': handler });
}
function showPage(resp) {
const pageTo = (page) => () => {
$('#prev , #next'). unbind('click ');call(resp.query , page , showPage );
};
displayTable(resp);
$('#prev').one('click ', pageTo(resp.prev ));
$('#next').one('click ', pageTo(resp.next ));
}
$('#filter ').keyup((ev) => {
$('#prev , #next'). unbind('click ');call($(ev.target ).val(), 1, showPage );
});
Call Stack Event Queue
closure3 keyup 7→ closure1 prev 7→ closure2
4 / 13
Sequencing Filtering + Paging (Using Callbacks)
function call(query , page , handler) {
$.ajax({ 'url': makeURL(query , page), 'success ': handler });
}
function showPage(resp) {
const pageTo = (page) => () => {
$('#prev , #next'). unbind('click ');call(resp.query , page , showPage );
};
displayTable(resp);
$('#prev').one('click ', pageTo(resp.prev ));
$('#next').one('click ', pageTo(resp.next ));
}
$('#filter ').keyup((ev) => {
$('#prev , #next'). unbind('click ');call($(ev.target ).val(), 1, showPage );
});
Call Stack Event Queue
callclosure3 keyup 7→ closure1 ajax 7→ showPage
4 / 13
Callbacks - The Problems
Lose flow of (synchronous) code
Event registration/de-registration is ad-hocLose imperative exception semantics
try {
User.findOne ({name: 'Eric'}, (err , obj) => {
if (err != null) { throw err; }
else if (obj == null) { throw new Error('Not Found '); }
else { /* ... */ }
});
} catch (err) {
/* ... */
}
20% of function calls provide a callback72% of callbacks are asynchronous48% of callbacks are anonymous
(Gallaba et al.)
5 / 13
Callbacks - The Problems
Lose flow of (synchronous) codeEvent registration/de-registration is ad-hoc
Lose imperative exception semantics
try {
User.findOne ({name: 'Eric'}, (err , obj) => {
if (err != null) { throw err; }
else if (obj == null) { throw new Error('Not Found '); }
else { /* ... */ }
});
} catch (err) {
/* ... */
}
20% of function calls provide a callback72% of callbacks are asynchronous48% of callbacks are anonymous
(Gallaba et al.)
5 / 13
Callbacks - The Problems
Lose flow of (synchronous) codeEvent registration/de-registration is ad-hoc
Lose imperative exception semantics
try {
User.findOne ({name: 'Eric'}, (err , obj) => {
if (err != null) { throw err; }
else if (obj == null) { throw new Error('Not Found '); }
else { /* ... */ }
});
} catch (err) {
/* ... */
}
20% of function calls provide a callback72% of callbacks are asynchronous48% of callbacks are anonymous
(Gallaba et al.)
5 / 13
Callbacks - The Problems
Lose flow of (synchronous) codeEvent registration/de-registration is ad-hoc
Lose imperative exception semantics
try {
User.findOne ({name: 'Eric'}, (err , obj) => {
if (err != null) { throw err; }
else if (obj == null) { throw new Error('Not Found '); }
else { /* ... */ }
});
} catch (err) {
/* ... */
}
20% of function calls provide a callback72% of callbacks are asynchronous48% of callbacks are anonymous
(Gallaba et al.)
5 / 13
Promises – A Better Abstraction
PendingWait toSettle
Fulfilled
Rejected
resolve(promise)
resolve(value)
reject(reason)
Settled
var promise = new Promise((resolve , reject) => {
conn.query('SELECT * from items ', (err , row) => {
if (!!err) resolve(row);
else reject(err);
});
});
promise.then(onSuccess );
promise.then(onSuccess , onError );
promise.then(onSuccess ). catch(onError );
6 / 13
Promises – A Better Abstraction
PendingWait toSettle
Fulfilled
Rejected
resolve(promise)
resolve(value)
reject(reason)
Settled
var promise = new Promise((resolve , reject) => {
conn.query('SELECT * from items ', (err , row) => {
if (!!err) resolve(row);
else reject(err);
});
});
promise.then(onSuccess );
promise.then(onSuccess , onError );
promise.then(onSuccess ). catch(onError );
6 / 13
Promises – A Better Abstraction
PendingWait toSettle
Fulfilled
Rejected
resolve(promise)
resolve(value)
reject(reason)
Settled
var promise = new Promise((resolve , reject) => {
conn.query('SELECT * from items ', (err , row) => {
if (!!err) resolve(row);
else reject(err);
});
});
promise.then(onSuccess );
promise.then(onSuccess , onError );
promise.then(onSuccess ). catch(onError );
6 / 13
Arrows – Attacking the Abstraction Differently
fv v ′
7 / 13
Arrows – Attacking the Abstraction Differently
fv v ′
Invoked Many Times
7 / 13
Arrows – Attacking the Abstraction Differently
fv v ′ g v ′′
7 / 13
Arrows – Attacking the Abstraction Differently
fv v ′ g v ′′
Synchronous
7 / 13
Arrows – Attacking the Abstraction Differently
fv v ′ g v ′′
Synchronous
delay
event
ajax
query
v
Elem
Conf
String
v
Event
Response
Response
Asynchronous
7 / 13
Arrows - Async Machine Composition
ajax
explode
nth(3)
handle
onclick onclick
nth(1) nth(2)
(Str ,Num) ;((Str ,Num), (Str ,Num), [Item])
[Item] ; >(α, β, [Item]) ; (α, β, [Item])
(Str ,Num) ;((Str ,Num), (Str ,Num), [Item])
(Str ,Num) ; >
(µ, ν, θ) ; µ \ {µ ≤ (Str ,Num)} and(µ, ν, θ) ; ν \ {ν ≤ (Str ,Num)}
(Str ,Num) ≤ µ ≤ (Str ,Num)(Str ,Num) ≤ ν ≤ (Str ,Num)> 6≤ (µ, ν, θ)
let page = Arrow.fix(a => Arrow.seq([
ajax ,
explode ,
Arrow.seq([
new NthArrow (3),
handle
]). remember(),
Arrow.any([
new NthArrow (1). onclick('#prev'),new NthArrow (2). onclick('#next'),
]),
a,
]));
let filter = Arrow.fix(a => Arrow.any([
Arrow.seq([
getQueryAndPage ,
page
]). noemit(),
a
]). onkeyup('#filter '));
filter.run ();
Composition Time
Execution Time
8 / 13
Arrows - Async Machine Composition
ajax
explode
nth(3)
handle
onclick onclick
nth(1) nth(2)
(Str ,Num) ;((Str ,Num), (Str ,Num), [Item])
[Item] ; >(α, β, [Item]) ; (α, β, [Item])
(Str ,Num) ;((Str ,Num), (Str ,Num), [Item])
(Str ,Num) ; >
(µ, ν, θ) ; µ \ {µ ≤ (Str ,Num)} and(µ, ν, θ) ; ν \ {ν ≤ (Str ,Num)}
(Str ,Num) ≤ µ ≤ (Str ,Num)(Str ,Num) ≤ ν ≤ (Str ,Num)> 6≤ (µ, ν, θ)
let page = Arrow.fix(a => Arrow.seq([
ajax ,
explode ,
Arrow.seq([
new NthArrow (3),
handle
]). remember(),
Arrow.any([
new NthArrow (1). onclick('#prev'),new NthArrow (2). onclick('#next'),
]),
a,
]));
let filter = Arrow.fix(a => Arrow.any([
Arrow.seq([
getQueryAndPage ,
page
]). noemit(),
a
]). onkeyup('#filter '));
filter.run ();
8 / 13
Arrows - Async Machine Composition
ajax
explode
nth(3)
handle
onclick onclick
nth(1) nth(2)
(Str ,Num) ;((Str ,Num), (Str ,Num), [Item])
[Item] ; >(α, β, [Item]) ; (α, β, [Item])
(Str ,Num) ;((Str ,Num), (Str ,Num), [Item])
(Str ,Num) ; >
(µ, ν, θ) ; µ \ {µ ≤ (Str ,Num)} and(µ, ν, θ) ; ν \ {ν ≤ (Str ,Num)}
(Str ,Num) ≤ µ ≤ (Str ,Num)(Str ,Num) ≤ ν ≤ (Str ,Num)> 6≤ (µ, ν, θ)
let page = Arrow.fix(a => Arrow.seq([
ajax ,
explode ,
Arrow.seq([
new NthArrow (3),
handle
]). remember(),
Arrow.any([
new NthArrow (1). onclick('#prev'),new NthArrow (2). onclick('#next'),
]),
a,
]));
let filter = Arrow.fix(a => Arrow.any([
Arrow.seq([
getQueryAndPage ,
page
]). noemit(),
a
]). onkeyup('#filter '));
filter.run ();
8 / 13
Arrows - Async Machine Composition
ajax
explode
nth(3)
handle
onclick onclick
nth(1) nth(2)
(Str ,Num) ;((Str ,Num), (Str ,Num), [Item])
[Item] ; >(α, β, [Item]) ; (α, β, [Item])
(Str ,Num) ;((Str ,Num), (Str ,Num), [Item])
(Str ,Num) ; >
(µ, ν, θ) ; µ \ {µ ≤ (Str ,Num)} and(µ, ν, θ) ; ν \ {ν ≤ (Str ,Num)}
(Str ,Num) ≤ µ ≤ (Str ,Num)(Str ,Num) ≤ ν ≤ (Str ,Num)> 6≤ (µ, ν, θ)
let page = Arrow.fix(a => Arrow.seq([
ajax ,
explode ,
Arrow.seq([
new NthArrow (3),
handle
]). remember(),
Arrow.any([
new NthArrow (1). onclick('#prev'),new NthArrow (2). onclick('#next'),
]),
a,
]));
let filter = Arrow.fix(a => Arrow.any([
Arrow.seq([
getQueryAndPage ,
page
]). noemit(),
a
]). onkeyup('#filter '));
filter.run ();
8 / 13
Arrows - Async Machine Composition
ajax
explode
nth(3)
handle
onclick onclick
nth(1) nth(2)
(Str ,Num) ;((Str ,Num), (Str ,Num), [Item])
[Item] ; >(α, β, [Item]) ; (α, β, [Item])
(Str ,Num) ;((Str ,Num), (Str ,Num), [Item])
(Str ,Num) ; >
(µ, ν, θ) ; µ \ {µ ≤ (Str ,Num)} and(µ, ν, θ) ; ν \ {ν ≤ (Str ,Num)}
(Str ,Num) ≤ µ ≤ (Str ,Num)(Str ,Num) ≤ ν ≤ (Str ,Num)> 6≤ (µ, ν, θ)
let page = Arrow.fix(a => Arrow.seq([
ajax ,
explode ,
Arrow.seq([
new NthArrow (3),
handle
]). remember(),
Arrow.any([
new NthArrow (1). onclick('#prev'),new NthArrow (2). onclick('#next'),
]),
a,
]));
let filter = Arrow.fix(a => Arrow.any([
Arrow.seq([
getQueryAndPage ,
page
]). noemit(),
a
]). onkeyup('#filter '));
filter.run ();
8 / 13
Arrows - Async Machine Composition
ajax
explode
nth(3)
handle
onclick onclick
nth(1) nth(2)
(Str ,Num) ;((Str ,Num), (Str ,Num), [Item])
[Item] ; >(α, β, [Item]) ; (α, β, [Item])
(Str ,Num) ;((Str ,Num), (Str ,Num), [Item])
(Str ,Num) ; >
(µ, ν, θ) ; µ \ {µ ≤ (Str ,Num)} and(µ, ν, θ) ; ν \ {ν ≤ (Str ,Num)}
(Str ,Num) ≤ µ ≤ (Str ,Num)(Str ,Num) ≤ ν ≤ (Str ,Num)> 6≤ (µ, ν, θ)
let page = Arrow.fix(a => Arrow.seq([
ajax ,
explode ,
Arrow.seq([
new NthArrow (3),
handle
]). remember(),
Arrow.any([
new NthArrow (1). onclick('#prev'),new NthArrow (2). onclick('#next'),
]),
a,
]));
let filter = Arrow.fix(a => Arrow.any([
Arrow.seq([
getQueryAndPage ,
page
]). noemit(),
a
]). onkeyup('#filter '));
filter.run ();
8 / 13
Arrows - Async Machine Composition
ajax
explode
nth(3)
handle
onclick onclick
nth(1) nth(2)
(Str ,Num) ;((Str ,Num), (Str ,Num), [Item])
[Item] ; >(α, β, [Item]) ; (α, β, [Item])
(Str ,Num) ;((Str ,Num), (Str ,Num), [Item])
(Str ,Num) ; >
(µ, ν, θ) ; µ \ {µ ≤ (Str ,Num)} and(µ, ν, θ) ; ν \ {ν ≤ (Str ,Num)}
(Str ,Num) ≤ µ ≤ (Str ,Num)(Str ,Num) ≤ ν ≤ (Str ,Num)> 6≤ (µ, ν, θ)
let page = Arrow.fix(a => Arrow.seq([
ajax ,
explode ,
Arrow.seq([
new NthArrow (3),
handle
]). remember(),
Arrow.any([
new NthArrow (1). onclick('#prev'),new NthArrow (2). onclick('#next'),
]),
a,
]));
let filter = Arrow.fix(a => Arrow.any([
Arrow.seq([
getQueryAndPage ,
page
]). noemit(),
a
]). onkeyup('#filter '));
filter.run ();
8 / 13
Composition-Time Inference
ajax
explode
nth(3)
handle
onclick onclick
nth(1) nth(2)
(Str ,Num) ;((Str ,Num), (Str ,Num), [Item])
[Item] ; >
(α, β, [Item]) ; (α, β, [Item])
(Str ,Num) ;((Str ,Num), (Str ,Num), [Item])
(Str ,Num) ; >
(µ, ν, θ) ; µ \ {µ ≤ (Str ,Num)} and(µ, ν, θ) ; ν \ {ν ≤ (Str ,Num)}
(Str ,Num) ≤ µ ≤ (Str ,Num)(Str ,Num) ≤ ν ≤ (Str ,Num)> 6≤ (µ, ν, θ)
let page = Arrow.fix(a => Arrow.seq([
ajax ,
explode ,
Arrow.seq([
new NthArrow (3),
handle
]). remember(),
Arrow.any([
new NthArrow (1). onclick('#prev'),new NthArrow (2). onclick('#next'),
]),
a,
]));
let filter = Arrow.fix(a => Arrow.any([
Arrow.seq([
getQueryAndPage ,
page
]). noemit(),
a
]). onkeyup('#filter '));
filter.run ();
8 / 13
Composition-Time Inference
ajax
explode
nth(3)
handle
onclick onclick
nth(1) nth(2)
(Str ,Num) ;((Str ,Num), (Str ,Num), [Item])
[Item] ; >
(α, β, [Item]) ; (α, β, [Item])
(Str ,Num) ;((Str ,Num), (Str ,Num), [Item])
(Str ,Num) ; >
(µ, ν, θ) ; µ \ {µ ≤ (Str ,Num)} and(µ, ν, θ) ; ν \ {ν ≤ (Str ,Num)}
(Str ,Num) ≤ µ ≤ (Str ,Num)(Str ,Num) ≤ ν ≤ (Str ,Num)> 6≤ (µ, ν, θ)
let page = Arrow.fix(a => Arrow.seq([
ajax ,
explode ,
Arrow.seq([
new NthArrow (3),
handle
]). remember(),
Arrow.any([
new NthArrow (1). onclick('#prev'),new NthArrow (2). onclick('#next'),
]),
a,
]));
let filter = Arrow.fix(a => Arrow.any([
Arrow.seq([
getQueryAndPage ,
page
]). noemit(),
a
]). onkeyup('#filter '));
filter.run ();
8 / 13
Composition-Time Inference
ajax
explode
nth(3)
handle
onclick onclick
nth(1) nth(2)
(Str ,Num) ;((Str ,Num), (Str ,Num), [Item])
[Item] ; >(α, β, [Item]) ; (α, β, [Item])
(Str ,Num) ;((Str ,Num), (Str ,Num), [Item])
(Str ,Num) ; >
(µ, ν, θ) ; µ \ {µ ≤ (Str ,Num)} and(µ, ν, θ) ; ν \ {ν ≤ (Str ,Num)}
(Str ,Num) ≤ µ ≤ (Str ,Num)(Str ,Num) ≤ ν ≤ (Str ,Num)> 6≤ (µ, ν, θ)
let page = Arrow.fix(a => Arrow.seq([
ajax ,
explode ,
Arrow.seq([
new NthArrow (3),
handle
]). remember(),
Arrow.any([
new NthArrow (1). onclick('#prev'),new NthArrow (2). onclick('#next'),
]),
a,
]));
let filter = Arrow.fix(a => Arrow.any([
Arrow.seq([
getQueryAndPage ,
page
]). noemit(),
a
]). onkeyup('#filter '));
filter.run ();
8 / 13
Composition-Time Inference
ajax
explode
nth(3)
handle
onclick onclick
nth(1) nth(2)
(Str ,Num) ;((Str ,Num), (Str ,Num), [Item])
[Item] ; >(α, β, [Item]) ; (α, β, [Item])
(Str ,Num) ;((Str ,Num), (Str ,Num), [Item])
(Str ,Num) ; >
(µ, ν, θ) ; µ \ {µ ≤ (Str ,Num)} and(µ, ν, θ) ; ν \ {ν ≤ (Str ,Num)}
(Str ,Num) ≤ µ ≤ (Str ,Num)(Str ,Num) ≤ ν ≤ (Str ,Num)
> 6≤ (µ, ν, θ)
let page = Arrow.fix(a => Arrow.seq([
ajax ,
explode ,
Arrow.seq([
new NthArrow (3),
handle
]). remember(),
Arrow.any([
new NthArrow (1). onclick('#prev'),new NthArrow (2). onclick('#next'),
]),
a,
]));
let filter = Arrow.fix(a => Arrow.any([
Arrow.seq([
getQueryAndPage ,
page
]). noemit(),
a
]). onkeyup('#filter '));
filter.run ();
8 / 13
Type Clash Detection
ajax
explode
nth(3)
handle
onclick onclick
nth(1) nth(2)
(Str ,Num) ;((Str ,Num), (Str ,Num), [Item])
[Item] ; >(α, β, [Item]) ; (α, β, [Item])
(Str ,Num) ;((Str ,Num), (Str ,Num), [Item])
(Str ,Num) ; >
(µ, ν, θ) ; µ \ {µ ≤ (Str ,Num)} and(µ, ν, θ) ; ν \ {ν ≤ (Str ,Num)}
(Str ,Num) ≤ µ ≤ (Str ,Num)(Str ,Num) ≤ ν ≤ (Str ,Num)
> 6≤ (µ, ν, θ)
let page = Arrow.fix(a => Arrow.seq([
ajax ,
explode ,
Arrow.seq([
new NthArrow (3),
handle
]). remember(),
Arrow.any([
new NthArrow (1). onclick('#prev'),new NthArrow (2). onclick('#next'),
]),
a,
]));
let filter = Arrow.fix(a => Arrow.any([
Arrow.seq([
getQueryAndPage ,
page
]). noemit(),
a
]). onkeyup('#filter '));
filter.run ();
8 / 13
Adding Optimizations
9 / 13
Cached Ajax Responses
lookup
ajax store nth(2)
cache
const cachedAjax = Arrow.catch(lookup , Arrow.seq([
ajax.carry(),
store.remember(),
new NthArrow (2)
]));
function lookup(key) {
if (key in cache) return cache[key];
else throw key;
}
10 / 13
Cached Ajax Responses
lookup
ajax store nth(2)
cache
const cachedAjax = Arrow.catch(lookup , Arrow.seq([
ajax.carry(),
store.remember(),
new NthArrow (2)
]));
function lookup(key) {
if (key in cache) return cache[key];
else throw key;
}
10 / 13
Cached Ajax Responses
lookup
ajax store nth(2)
cache
const cachedAjax = Arrow.catch(lookup , Arrow.seq([
ajax.carry(),
store.remember(),
new NthArrow (2)
]));
function lookup(key) {
if (key in cache) return cache[key];
else throw key;
}
10 / 13
Pre-Fetching Next Page
handle
onclick
nth(1)
nth(2)
cachedAjax onclick
Arrows.any([
new NthArrow (1). onclick('#prev'),new NthArrow (2). seq(Arrow.fanout ([
ajaxOrCached ,
onclick('#next')]). noemit (). remember ())
]);
11 / 13
Pre-Fetching Next Page
handle
onclick
nth(1)
nth(2)
cachedAjax onclick
Arrows.any([
new NthArrow (1). onclick('#prev'),new NthArrow (2). seq(Arrow.fanout ([
ajaxOrCached ,
onclick('#next')]). noemit (). remember ())
]);
11 / 13
Pre-Fetching Next Page
handle
onclick
nth(1)
nth(2)
cachedAjax onclick
Arrows.any([
new NthArrow (1). onclick('#prev'),new NthArrow (2). seq(Arrow.fanout ([
ajaxOrCached ,
onclick('#next')]). noemit (). remember ())
]);
11 / 13
Pre-Fetching Next Page
handle
onclick
nth(1)
nth(2)
cachedAjax onclick
Arrows.any([
new NthArrow (1). onclick('#prev'),new NthArrow (2). seq(Arrow.fanout ([
ajaxOrCached ,
onclick('#next')]). noemit (). remember ())
]);
11 / 13
No In-Flight Requests as User Types
#filter
keyup
Q and P
wait
page
let filter = Arrow.fix(a => Arrow.any([
a,
Arrow.seq([
getQueryAndPage ,
new DelayArrow (400),
page
]). noemit(),
]). onkeyup('#filter '));
12 / 13
No In-Flight Requests as User Types
#filter
keyup
Q and P wait page
let filter = Arrow.fix(a => Arrow.any([
a,
Arrow.seq([
getQueryAndPage ,
new DelayArrow (400),
page
]). noemit(),
]). onkeyup('#filter '));
12 / 13
No In-Flight Requests as User Types
#filter
keyup
Q and P wait page
#filter
keyup
ad infinitum
let filter = Arrow.fix(a => Arrow.any([
a,
Arrow.seq([
getQueryAndPage ,
new DelayArrow (400),
page
]). noemit(),
]). onkeyup('#filter '));
12 / 13
Conclusion
Composing an interaction machine is a powerful shift in abstractionOpens up opportunity for ‘static’ benefits
Examples, papers, and code available athttp://arrows.eric-fritz.com
Questions?
13 / 13
Conclusion
Composing an interaction machine is a powerful shift in abstractionOpens up opportunity for ‘static’ benefits
Examples, papers, and code available athttp://arrows.eric-fritz.com
Questions?
13 / 13
Conclusion
Composing an interaction machine is a powerful shift in abstractionOpens up opportunity for ‘static’ benefits
Examples, papers, and code available athttp://arrows.eric-fritz.com
Questions?
13 / 13