From a0143855f3baa0777b70e6f919055cc3c4a58a9e Mon Sep 17 00:00:00 2001 From: Tony Garnock-Jones Date: Mon, 11 Jul 2016 12:16:51 -0400 Subject: [PATCH] Update --- examples/location/index.expanded.js | 4 +- examples/todo/index.expanded.js | 48 ++++----- examples/two-buyer-protocol/index.expanded.js | 90 +++++++++++------ examples/two-buyer-protocol/index.js | 97 +++++++++++++------ examples/two-buyer-protocol/index.md | 97 +++++++++++++------ 5 files changed, 215 insertions(+), 121 deletions(-) diff --git a/examples/location/index.expanded.js b/examples/location/index.expanded.js index c7ab8db..9c43c40 100644 --- a/examples/location/index.expanded.js +++ b/examples/location/index.expanded.js @@ -105,7 +105,7 @@ Syndicate.Actor.createFacet() }); } } -var _cachedAssertion1468253137502_0 = (function() { var _ = Syndicate.__; return fromBroker(wsurl, locationRecord(id, email, _, _, _)); })(); +var _cachedAssertion1468253787297_0 = (function() { var _ = Syndicate.__; return fromBroker(wsurl, locationRecord(id, email, _, _, _)); })(); Syndicate.Actor.createFacet() .addInitBlock((function() { marker.addListener('click', Syndicate.Dataspace.wrap(function () { @@ -132,6 +132,6 @@ Syndicate.Actor.createFacet() marker.setMap(null); if (selectedMarker === marker) selectedMarker = null; })) -.onEvent(true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1468253137502_0, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1468253137502_0, metalevel: 0 }; }), (function() {})).completeBuild(); })).completeBuild(); })(); +.onEvent(true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1468253787297_0, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1468253787297_0, metalevel: 0 }; }), (function() {})).completeBuild(); })).completeBuild(); })(); }); }).startStepping(); diff --git a/examples/todo/index.expanded.js b/examples/todo/index.expanded.js index f6d5244..f675bad 100644 --- a/examples/todo/index.expanded.js +++ b/examples/todo/index.expanded.js @@ -54,10 +54,10 @@ function todoListItemView(id) { (function () { Syndicate.Actor.createFacet() .onEvent(false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(todo(id, _, _), 0); }), (function() { var _ = Syndicate.__; return { assertion: todo(id, (Syndicate._$("title")), (Syndicate._$("completed"))), metalevel: 0 }; }), (function(title, completed) { -var _cachedAssertion1468253138670_0 = (function() { var _ = Syndicate.__; return todo(id, title, completed); })(); +var _cachedAssertion1468253788491_0 = (function() { var _ = Syndicate.__; return todo(id, title, completed); })(); Syndicate.Actor.createFacet() .onEvent(false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(show(completed), 0); }), (function() { var _ = Syndicate.__; return { assertion: show(completed), metalevel: 0 }; }), (function() { -var _cachedAssertion1468253138670_1 = (function() { var _ = Syndicate.__; return show(completed); })(); +var _cachedAssertion1468253788491_1 = (function() { var _ = Syndicate.__; return show(completed); })(); Syndicate.Actor.createFacet() .addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(this.ui.html('.todo-list', Mustache.render(getTemplate(this.editing @@ -70,8 +70,8 @@ Syndicate.Actor.createFacet() checked: completed ? "checked" : "", }), id), 0); })) -.onEvent(true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1468253138670_1, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1468253138670_1, metalevel: 0 }; }), (function() {})).completeBuild(); })) -.onEvent(true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1468253138670_0, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1468253138670_0, metalevel: 0 }; }), (function() {})).completeBuild(); })) +.onEvent(true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1468253788491_1, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1468253788491_1, metalevel: 0 }; }), (function() {})).completeBuild(); })) +.onEvent(true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1468253788491_0, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1468253788491_0, metalevel: 0 }; }), (function() {})).completeBuild(); })) .onEvent(false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(this.ui.event('.toggle', 'change', _), 0); }), (function() { var _ = Syndicate.__; return { assertion: this.ui.event('.toggle', 'change', (Syndicate._$("e"))), metalevel: 0 }; }), (function(e) { Syndicate.Dataspace.send(setCompleted(id, e.target.checked)); })) @@ -119,31 +119,31 @@ Syndicate.Actor.createFacet() (function () { Syndicate.Actor.createFacet() .onEvent(false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(activeTodoCount(_), 0); }), (function() { var _ = Syndicate.__; return { assertion: activeTodoCount((Syndicate._$("count"))), metalevel: 0 }; }), (function(count) { -var _cachedAssertion1468253138670_2 = (function() { var _ = Syndicate.__; return activeTodoCount(count); })(); +var _cachedAssertion1468253788491_2 = (function() { var _ = Syndicate.__; return activeTodoCount(count); })(); Syndicate.Actor.createFacet() .addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(this.ui.context('count').html('.todo-count strong', '' + count), 0); })) .addAssertion((function() { var _ = Syndicate.__; return (count !== 1) ? Syndicate.Patch.assert(, 0) : Syndicate.Patch.emptyPatch; })) -.onEvent(true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1468253138670_2, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1468253138670_2, metalevel: 0 }; }), (function() {})).completeBuild(); })) +.onEvent(true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1468253788491_2, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1468253788491_2, metalevel: 0 }; }), (function() {})).completeBuild(); })) .onEvent(false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(totalTodoCount(0), 0); }), (function() { var _ = Syndicate.__; return { assertion: totalTodoCount(0), metalevel: 0 }; }), (function() { -var _cachedAssertion1468253138670_3 = (function() { var _ = Syndicate.__; return totalTodoCount(0); })(); +var _cachedAssertion1468253788491_3 = (function() { var _ = Syndicate.__; return totalTodoCount(0); })(); Syndicate.Actor.createFacet() .addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(Syndicate.UI.uiAttribute('section.main', 'class', 'hidden'), 0); })) .addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(Syndicate.UI.uiAttribute('footer.footer', 'class', 'hidden'), 0); })) -.onEvent(true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1468253138670_3, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1468253138670_3, metalevel: 0 }; }), (function() {})).completeBuild(); })) +.onEvent(true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1468253788491_3, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1468253788491_3, metalevel: 0 }; }), (function() {})).completeBuild(); })) .onEvent(false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(completedTodoCount(0), 0); }), (function() { var _ = Syndicate.__; return { assertion: completedTodoCount(0), metalevel: 0 }; }), (function() { -var _cachedAssertion1468253138670_4 = (function() { var _ = Syndicate.__; return completedTodoCount(0); })(); +var _cachedAssertion1468253788491_4 = (function() { var _ = Syndicate.__; return completedTodoCount(0); })(); Syndicate.Actor.createFacet() .addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(Syndicate.UI.uiAttribute('button.clear-completed', 'class', 'hidden'), 0); })) -.onEvent(true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1468253138670_4, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1468253138670_4, metalevel: 0 }; }), (function() {})).completeBuild(); })) +.onEvent(true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1468253788491_4, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1468253788491_4, metalevel: 0 }; }), (function() {})).completeBuild(); })) .onEvent(false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.UI.globalEvent('button.clear-completed', 'click', _), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.UI.globalEvent('button.clear-completed', 'click', _), metalevel: 0 }; }), (function() { Syndicate.Dataspace.send(clearCompletedTodos()); })) .onEvent(false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(allCompleted(), 0); }), (function() { var _ = Syndicate.__; return { assertion: allCompleted(), metalevel: 0 }; }), (function() { -var _cachedAssertion1468253138670_5 = (function() { var _ = Syndicate.__; return allCompleted(); })(); +var _cachedAssertion1468253788491_5 = (function() { var _ = Syndicate.__; return allCompleted(); })(); Syndicate.Actor.createFacet() .addInitBlock((function() { Syndicate.Dataspace.send(Syndicate.UI.setProperty('.toggle-all', 'checked', true)); })) .addDoneBlock((function() { Syndicate.Dataspace.send(Syndicate.UI.setProperty('.toggle-all', 'checked', false)); })) -.onEvent(true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1468253138670_5, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1468253138670_5, metalevel: 0 }; }), (function() {})).completeBuild(); })) +.onEvent(true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1468253788491_5, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1468253788491_5, metalevel: 0 }; }), (function() {})).completeBuild(); })) .onEvent(false, "message", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.UI.globalEvent('.toggle-all', 'change', _), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.UI.globalEvent('.toggle-all', 'change', (Syndicate._$("e"))), metalevel: 0 }; }), (function(e) { Syndicate.Dataspace.send(setAllCompleted(e.target.checked)); })) @@ -169,27 +169,27 @@ Syndicate.Actor.createFacet() (function () { Syndicate.Actor.createFacet() .onEvent(false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.UI.locationHash(_), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.UI.locationHash((Syndicate._$("hash"))), metalevel: 0 }; }), (function(hash) { -var _cachedAssertion1468253138670_6 = (function() { var _ = Syndicate.__; return Syndicate.UI.locationHash(hash); })(); +var _cachedAssertion1468253788491_6 = (function() { var _ = Syndicate.__; return Syndicate.UI.locationHash(hash); })(); Syndicate.Actor.createFacet() .addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(Syndicate.UI.uiAttribute('ul.filters > li > a[href="#'+hash+'"]', 'class', 'selected'), 0); })) -.onEvent(true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1468253138670_6, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1468253138670_6, metalevel: 0 }; }), (function() {})).completeBuild(); })) +.onEvent(true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1468253788491_6, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1468253788491_6, metalevel: 0 }; }), (function() {})).completeBuild(); })) .onEvent(false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.UI.locationHash('/'), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.UI.locationHash('/'), metalevel: 0 }; }), (function() { -var _cachedAssertion1468253138670_7 = (function() { var _ = Syndicate.__; return Syndicate.UI.locationHash('/'); })(); +var _cachedAssertion1468253788491_7 = (function() { var _ = Syndicate.__; return Syndicate.UI.locationHash('/'); })(); Syndicate.Actor.createFacet() .addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(show(true), 0); })) .addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(show(false), 0); })) -.onEvent(true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1468253138670_7, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1468253138670_7, metalevel: 0 }; }), (function() {})).completeBuild(); })) +.onEvent(true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1468253788491_7, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1468253788491_7, metalevel: 0 }; }), (function() {})).completeBuild(); })) .onEvent(false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.UI.locationHash('/active'), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.UI.locationHash('/active'), metalevel: 0 }; }), (function() { -var _cachedAssertion1468253138670_8 = (function() { var _ = Syndicate.__; return Syndicate.UI.locationHash('/active'); })(); +var _cachedAssertion1468253788491_8 = (function() { var _ = Syndicate.__; return Syndicate.UI.locationHash('/active'); })(); Syndicate.Actor.createFacet() .addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(show(false), 0); })) -.onEvent(true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1468253138670_8, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1468253138670_8, metalevel: 0 }; }), (function() {})).completeBuild(); })) +.onEvent(true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1468253788491_8, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1468253788491_8, metalevel: 0 }; }), (function() {})).completeBuild(); })) .onEvent(false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.UI.locationHash('/completed'), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.UI.locationHash('/completed'), metalevel: 0 }; }), (function() { -var _cachedAssertion1468253138670_9 = (function() { var _ = Syndicate.__; return Syndicate.UI.locationHash('/completed'); })(); +var _cachedAssertion1468253788491_9 = (function() { var _ = Syndicate.__; return Syndicate.UI.locationHash('/completed'); })(); Syndicate.Actor.createFacet() .addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(show(true), 0); })) -.onEvent(true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1468253138670_9, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1468253138670_9, metalevel: 0 }; }), (function() {})).completeBuild(); })).completeBuild(); })(); +.onEvent(true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1468253788491_9, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1468253788491_9, metalevel: 0 }; }), (function() {})).completeBuild(); })).completeBuild(); })(); }); Syndicate.Actor.spawnActor(new Object(), function() { @@ -218,20 +218,20 @@ Syndicate.Actor.createFacet() todoListItemModel(db.nextId++, title, false); })) .onEvent(false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(todo(_, _, _), 0); }), (function() { var _ = Syndicate.__; return { assertion: todo((Syndicate._$("id")), _, _), metalevel: 0 }; }), (function(id) { -var _cachedAssertion1468253138670_10 = (function() { var _ = Syndicate.__; return todo(id, _, _); })(); +var _cachedAssertion1468253788491_10 = (function() { var _ = Syndicate.__; return todo(id, _, _); })(); Syndicate.Actor.createFacet() .onEvent(false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(todo(id, _, _), 0); }), (function() { var _ = Syndicate.__; return { assertion: todo(id, (Syndicate._$("title")), (Syndicate._$("completed"))), metalevel: 0 }; }), (function(title, completed) { -var _cachedAssertion1468253138670_11 = (function() { var _ = Syndicate.__; return todo(id, title, completed); })(); +var _cachedAssertion1468253788491_11 = (function() { var _ = Syndicate.__; return todo(id, title, completed); })(); Syndicate.Actor.createFacet() .addInitBlock((function() { db.todos[id] = {id: id, title: title, completed: completed}; localStorage['todos-syndicate'] = JSON.stringify(db); })) -.onEvent(true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1468253138670_11, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1468253138670_11, metalevel: 0 }; }), (function() {})).completeBuild(); })) +.onEvent(true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1468253788491_11, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1468253788491_11, metalevel: 0 }; }), (function() {})).completeBuild(); })) .addDoneBlock((function() { delete db.todos[id]; localStorage['todos-syndicate'] = JSON.stringify(db); })) -.onEvent(true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1468253138670_10, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1468253138670_10, metalevel: 0 }; }), (function() {})).completeBuild(); })).completeBuild(); })(); +.onEvent(true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1468253788491_10, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1468253788491_10, metalevel: 0 }; }), (function() {})).completeBuild(); })).completeBuild(); })(); }); }).startStepping(); diff --git a/examples/two-buyer-protocol/index.expanded.js b/examples/two-buyer-protocol/index.expanded.js index cb16841..e065f5c 100644 --- a/examples/two-buyer-protocol/index.expanded.js +++ b/examples/two-buyer-protocol/index.expanded.js @@ -2,13 +2,15 @@ var Syndicate = require('../../src/main.js'); /// --> +/// # {{ page.title }} +/// /// This is an extended two-buyer book-purchase protocol, based /// loosely on an example given in: /// /// > K. Honda, N. Yoshida, and M. Carbone, “Multiparty asynchronous /// > session types,” POPL 2008. -/// # The Scenario +/// ## The Scenario /// /// A book-seller responds to requests for book prices when asked. A /// pair of prospective buyers run through a shopping list. For each @@ -18,9 +20,9 @@ var Syndicate = require('../../src/main.js'); /// different split. If the second buyer agrees to a split, it then /// negotiates the purchase of the book with the book-seller. -/// # The Protocol +/// ## The Protocol -/// ## Role: SELLER +/// ### Role: SELLER /// /// - when interest in `bookQuote($title, _)` appears, /// asserts `bookQuote(title, Maybe Float)`, `false` meaning not available, @@ -29,17 +31,17 @@ var Syndicate = require('../../src/main.js'); /// asserts `order(title, offer-price, false, false)` for "no sale", otherwise /// `order(title, offer-price, PositiveInteger, String)`, an accepted sale. -/// ## Role: BUYER +/// ### Role: BUYER /// /// - observes `bookQuote(title, $price)` to learn prices. /// - observes `order(title, offer-price, $id, $delivery-date)` to make orders. -/// ## Role: SPLIT-PROPOSER +/// ### Role: SPLIT-PROPOSER /// /// - observes `splitProposal(title, asking-price, contribution, $accepted)` /// to make a split-proposal and learn whether it was accepted or not. -/// ## Role: SPLIT-DISPOSER +/// ### Role: SPLIT-DISPOSER /// /// - when interest in `splitProposal($title, $asking-price, $contribution, _)` /// appears, asserts `splitProposal(title, askingPrice, contribution, true)` @@ -48,7 +50,7 @@ var Syndicate = require('../../src/main.js'); /// `splitProposal(title, asking-price, contribution, false)` to indicate they /// are unwilling to go through with the deal. -/// # A Sample Run +/// ## A Sample Run /// /// Run the program with `../../bin/syndicatec index.js | node` from a /// checkout of the Syndicate repository. @@ -90,9 +92,9 @@ var Syndicate = require('../../src/main.js'); /// A has bought everything they wanted! /// The order for The Wind in the Willows has id 10001485, and will be delivered on March 9th -/// # The Code +/// ## The Code -/// ## Type Declarations +/// ### Type Declarations /// First, we declare *assertion types* for our protocol. @@ -101,7 +103,7 @@ var order = Syndicate.Struct.makeConstructor("order", ["title","price","id","del var splitProposal = Syndicate.Struct.makeConstructor("splitProposal", ["title","price","contribution","accepted"]); -/// ## Utilities +/// ### Utilities /// This routine is under consideration for possible addition to the /// core library. @@ -113,7 +115,7 @@ Syndicate.Actor.createFacet() .onEvent(true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.observe(P), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.observe(P), metalevel: 0 }; }), (function() {})).completeBuild(); })(); } -/// ## Implementation: SELLER +/// ### Implementation: SELLER function seller() { Syndicate.Actor.spawnActor(new Object(), function() { @@ -129,16 +131,23 @@ function seller() { }; this.nextOrderId = 10001483; +/// Looking up a price yields `false` if no such book is in our +/// inventory. + + this.priceOf = function (title) { + return (title in this.books) && this.books[title]; + }; + /// The seller responds to interest in bookQuotes by asserting a /// responsive record, if one exists. (function () { Syndicate.Actor.createFacet() .onEvent(false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.observe(bookQuote(_, _)), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.observe(bookQuote((Syndicate._$("title")), _)), metalevel: 0 }; }), (function(title) { -var _cachedAssertion1468253139911_0 = (function() { var _ = Syndicate.__; return Syndicate.observe(bookQuote(title, _)); })(); +var _cachedAssertion1468253789712_0 = (function() { var _ = Syndicate.__; return Syndicate.observe(bookQuote(title, _)); })(); Syndicate.Actor.createFacet() -.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(bookQuote(title, title in this.books ? this.books[title] : false), 0); })) -.onEvent(true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1468253139911_0, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1468253139911_0, metalevel: 0 }; }), (function() {})).completeBuild(); })).completeBuild(); })(); +.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(bookQuote(title, this.priceOf(title)), 0); })) +.onEvent(true, "retracted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(_cachedAssertion1468253789712_0, 0); }), (function() { var _ = Syndicate.__; return { assertion: _cachedAssertion1468253789712_0, metalevel: 0 }; }), (function() {})).completeBuild(); })).completeBuild(); })(); /// It also responds to order requests. @@ -149,9 +158,10 @@ Syndicate.Actor.createFacet() /// We cannot sell a book we do not have, and we will not sell for /// less than our asking price. - var askingPrice = title in this.books ? this.books[title] : false; + var askingPrice = this.priceOf(title); if ((askingPrice === false) || (offerPrice < askingPrice)) { - whileRelevantAssert(order(title, offerPrice, false, false)); + whileRelevantAssert( + order(title, offerPrice, false, false)); } else { /// But if we can sell it, we do so by allocating an order ID and @@ -161,14 +171,15 @@ Syndicate.Actor.createFacet() delete this.books[title]; Syndicate.Actor.spawnActor(new Object(), function() { - whileRelevantAssert(order(title, offerPrice, orderId, "March 9th")); + whileRelevantAssert( + order(title, offerPrice, orderId, "March 9th")); }); } })).completeBuild(); })(); }); } -/// ## Implementation: SPLIT-PROPOSER and book-quote-requestor +/// ### Implementation: SPLIT-PROPOSER and book-quote-requestor function buyerA() { Syndicate.Actor.spawnActor(new Object(), function() { @@ -205,7 +216,8 @@ Syndicate.Actor.createFacet() console.log("A learns that "+title+" is out-of-stock."); buyBooks(); } else { - console.log("A learns that the price of "+title+" is "+price); + console.log("A learns that the price of "+title+ + " is "+price); /// Next, repeatedly make split offers to a SPLIT-DISPOSER until /// either one is accepted, or the contribution from the @@ -236,19 +248,23 @@ Syndicate.Actor.createFacet() (function () { Syndicate.Actor.createFacet() .onEvent(true, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(splitProposal(title, price, contribution, true), 0); }), (function() { var _ = Syndicate.__; return { assertion: splitProposal(title, price, contribution, true), metalevel: 0 }; }), (function() { - console.log("A learns that the split-proposal for "+title+" was accepted"); + console.log("A learns that the split-proposal for "+ + title+" was accepted"); buyBooks(); })) .onEvent(true, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(splitProposal(title, price, contribution, false), 0); }), (function() { var _ = Syndicate.__; return { assertion: splitProposal(title, price, contribution, false), metalevel: 0 }; }), (function() { - console.log("A learns that the split-proposal for "+title+" was rejected"); - trySplit(title, price, contribution + ((price - contribution) / 2)); + console.log("A learns that the split-proposal for "+ + title+" was rejected"); + trySplit(title, + price, + contribution + ((price - contribution) / 2)); })).completeBuild(); })(); } } }); } -/// ## Implementation: SPLIT-DISPOSER and BUYER +/// ### Implementation: SPLIT-DISPOSER and BUYER function buyerB() { Syndicate.Actor.spawnActor(new Object(), function() { @@ -263,16 +279,24 @@ function buyerB() { (function () { Syndicate.Actor.createFacet() -.onEvent(false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.observe(splitProposal(_, _, _, _)), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.observe(splitProposal((Syndicate._$("title")), (Syndicate._$("price")), (Syndicate._$("theirContribution")), _)), metalevel: 0 }; }), (function(title, price, theirContribution) { +.onEvent(false, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(Syndicate.observe(splitProposal(_, + _, + _, + _)), 0); }), (function() { var _ = Syndicate.__; return { assertion: Syndicate.observe(splitProposal((Syndicate._$("title")), + (Syndicate._$("price")), + (Syndicate._$("theirContribution")), + _)), metalevel: 0 }; }), (function(title, price, theirContribution) { var myContribution = price - theirContribution; - console.log("B is being asked to contribute "+myContribution+" toward "+title+ - " at price "+price); + console.log("B is being asked to contribute "+myContribution+ + " toward "+title+" at price "+price); /// We may not be able to afford contributing this much. if (myContribution > this.funds) { - console.log("B hasn't enough funds ("+this.funds+" remaining)"); - whileRelevantAssert(splitProposal(title, price, theirContribution, false)); + console.log("B hasn't enough funds ("+this.funds+ + " remaining)"); + whileRelevantAssert( + splitProposal(title, price, theirContribution, false)); } else { /// But if we *can* afford it, update our remaining funds and spawn a @@ -280,13 +304,17 @@ Syndicate.Actor.createFacet() /// on a split. var remainingFunds = this.funds - myContribution; - console.log("B accepts the offer, leaving them with "+remainingFunds+" remaining funds"); + console.log("B accepts the offer, leaving them with "+ + remainingFunds+" remaining funds"); this.funds = remainingFunds; Syndicate.Actor.spawnActor(new Object(), function() { (function () { Syndicate.Actor.createFacet() -.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(splitProposal(title, price, theirContribution, true), 0); })) +.addAssertion((function() { var _ = Syndicate.__; return Syndicate.Patch.assert(splitProposal(title, + price, + theirContribution, + true), 0); })) .onEvent(true, "asserted", (function() { var _ = Syndicate.__; return Syndicate.Patch.sub(order(title, price, _, _), 0); }), (function() { var _ = Syndicate.__; return { assertion: order(title, price, (Syndicate._$("id")), (Syndicate._$("date"))), metalevel: 0 }; }), (function(id, date) { console.log("The order for "+title+" has id "+id+ ", and will be delivered on "+date); @@ -297,7 +325,7 @@ Syndicate.Actor.createFacet() }); } -/// ## Starting Configuration +/// ### Starting Configuration new Syndicate.Ground(function () { seller(); diff --git a/examples/two-buyer-protocol/index.js b/examples/two-buyer-protocol/index.js index 4987146..8844989 100644 --- a/examples/two-buyer-protocol/index.js +++ b/examples/two-buyer-protocol/index.js @@ -6,13 +6,15 @@ var Syndicate = require('../../src/main.js'); /// --> +/// # {{ page.title }} +/// /// This is an extended two-buyer book-purchase protocol, based /// loosely on an example given in: /// /// > K. Honda, N. Yoshida, and M. Carbone, “Multiparty asynchronous /// > session types,” POPL 2008. -/// # The Scenario +/// ## The Scenario /// /// A book-seller responds to requests for book prices when asked. A /// pair of prospective buyers run through a shopping list. For each @@ -22,9 +24,9 @@ var Syndicate = require('../../src/main.js'); /// different split. If the second buyer agrees to a split, it then /// negotiates the purchase of the book with the book-seller. -/// # The Protocol +/// ## The Protocol -/// ## Role: SELLER +/// ### Role: SELLER /// /// - when interest in `bookQuote($title, _)` appears, /// asserts `bookQuote(title, Maybe Float)`, `false` meaning not available, @@ -33,17 +35,17 @@ var Syndicate = require('../../src/main.js'); /// asserts `order(title, offer-price, false, false)` for "no sale", otherwise /// `order(title, offer-price, PositiveInteger, String)`, an accepted sale. -/// ## Role: BUYER +/// ### Role: BUYER /// /// - observes `bookQuote(title, $price)` to learn prices. /// - observes `order(title, offer-price, $id, $delivery-date)` to make orders. -/// ## Role: SPLIT-PROPOSER +/// ### Role: SPLIT-PROPOSER /// /// - observes `splitProposal(title, asking-price, contribution, $accepted)` /// to make a split-proposal and learn whether it was accepted or not. -/// ## Role: SPLIT-DISPOSER +/// ### Role: SPLIT-DISPOSER /// /// - when interest in `splitProposal($title, $asking-price, $contribution, _)` /// appears, asserts `splitProposal(title, askingPrice, contribution, true)` @@ -52,7 +54,7 @@ var Syndicate = require('../../src/main.js'); /// `splitProposal(title, asking-price, contribution, false)` to indicate they /// are unwilling to go through with the deal. -/// # A Sample Run +/// ## A Sample Run /// /// Run the program with `../../bin/syndicatec index.js | node` from a /// checkout of the Syndicate repository. @@ -94,9 +96,9 @@ var Syndicate = require('../../src/main.js'); /// A has bought everything they wanted! /// The order for The Wind in the Willows has id 10001485, and will be delivered on March 9th -/// # The Code +/// ## The Code -/// ## Type Declarations +/// ### Type Declarations /// First, we declare *assertion types* for our protocol. @@ -105,7 +107,7 @@ assertion type order(title, price, id, deliveryDate); assertion type splitProposal(title, price, contribution, accepted); -/// ## Utilities +/// ### Utilities /// This routine is under consideration for possible addition to the /// core library. @@ -118,7 +120,7 @@ function whileRelevantAssert(P) { } } -/// ## Implementation: SELLER +/// ### Implementation: SELLER function seller() { actor { @@ -134,26 +136,36 @@ function seller() { }; this.nextOrderId = 10001483; +/// Looking up a price yields `false` if no such book is in our +/// inventory. + + this.priceOf = function (title) { + return (title in this.books) && this.books[title]; + }; + /// The seller responds to interest in bookQuotes by asserting a /// responsive record, if one exists. react { during Syndicate.observe(bookQuote($title, _)) { - assert bookQuote(title, title in this.books ? this.books[title] : false); + assert bookQuote(title, this.priceOf(title)); } } /// It also responds to order requests. react { - on asserted Syndicate.observe(order($title, $offerPrice, _, _)) { + on asserted + Syndicate.observe(order($title, $offerPrice, _, _)) + { /// We cannot sell a book we do not have, and we will not sell for /// less than our asking price. - var askingPrice = title in this.books ? this.books[title] : false; + var askingPrice = this.priceOf(title); if ((askingPrice === false) || (offerPrice < askingPrice)) { - whileRelevantAssert(order(title, offerPrice, false, false)); + whileRelevantAssert( + order(title, offerPrice, false, false)); } else { /// But if we can sell it, we do so by allocating an order ID and @@ -163,7 +175,8 @@ function seller() { delete this.books[title]; actor { - whileRelevantAssert(order(title, offerPrice, orderId, "March 9th")); + whileRelevantAssert( + order(title, offerPrice, orderId, "March 9th")); } } } @@ -171,7 +184,7 @@ function seller() { } } -/// ## Implementation: SPLIT-PROPOSER and book-quote-requestor +/// ### Implementation: SPLIT-PROPOSER and book-quote-requestor function buyerA() { actor { @@ -207,7 +220,8 @@ function buyerA() { console.log("A learns that "+title+" is out-of-stock."); buyBooks(); } else { - console.log("A learns that the price of "+title+" is "+price); + console.log("A learns that the price of "+title+ + " is "+price); /// Next, repeatedly make split offers to a SPLIT-DISPOSER until /// either one is accepted, or the contribution from the @@ -237,14 +251,22 @@ function buyerA() { /// Make our proposal, and wait for a response. react until { - case asserted splitProposal(title, price, contribution, true) { - console.log("A learns that the split-proposal for "+title+" was accepted"); + case asserted + splitProposal(title, price, contribution, true) + { + console.log("A learns that the split-proposal for "+ + title+" was accepted"); buyBooks(); } - case asserted splitProposal(title, price, contribution, false) { - console.log("A learns that the split-proposal for "+title+" was rejected"); - trySplit(title, price, contribution + ((price - contribution) / 2)); + case asserted + splitProposal(title, price, contribution, false) + { + console.log("A learns that the split-proposal for "+ + title+" was rejected"); + trySplit(title, + price, + contribution + ((price - contribution) / 2)); } } } @@ -252,7 +274,7 @@ function buyerA() { } } -/// ## Implementation: SPLIT-DISPOSER and BUYER +/// ### Implementation: SPLIT-DISPOSER and BUYER function buyerB() { actor { @@ -266,16 +288,23 @@ function buyerB() { /// `splitProposal`. react { - on asserted Syndicate.observe(splitProposal($title, $price, $theirContribution, _)) { + on asserted + Syndicate.observe(splitProposal($title, + $price, + $theirContribution, + _)) + { var myContribution = price - theirContribution; - console.log("B is being asked to contribute "+myContribution+" toward "+title+ - " at price "+price); + console.log("B is being asked to contribute "+myContribution+ + " toward "+title+" at price "+price); /// We may not be able to afford contributing this much. if (myContribution > this.funds) { - console.log("B hasn't enough funds ("+this.funds+" remaining)"); - whileRelevantAssert(splitProposal(title, price, theirContribution, false)); + console.log("B hasn't enough funds ("+this.funds+ + " remaining)"); + whileRelevantAssert( + splitProposal(title, price, theirContribution, false)); } else { /// But if we *can* afford it, update our remaining funds and spawn a @@ -283,7 +312,8 @@ function buyerB() { /// on a split. var remainingFunds = this.funds - myContribution; - console.log("B accepts the offer, leaving them with "+remainingFunds+" remaining funds"); + console.log("B accepts the offer, leaving them with "+ + remainingFunds+" remaining funds"); this.funds = remainingFunds; actor { @@ -292,7 +322,10 @@ function buyerB() { /// While waiting for order confirmation, take the opportunity to /// signal to our SPLIT-PROPOSER that we accepted their proposal. - assert splitProposal(title, price, theirContribution, true); + assert splitProposal(title, + price, + theirContribution, + true); /// When order confirmation arrives, this purchase is completed. @@ -309,7 +342,7 @@ function buyerB() { } } -/// ## Starting Configuration +/// ### Starting Configuration ground dataspace { seller(); diff --git a/examples/two-buyer-protocol/index.md b/examples/two-buyer-protocol/index.md index d1f2225..c9d7c7c 100644 --- a/examples/two-buyer-protocol/index.md +++ b/examples/two-buyer-protocol/index.md @@ -6,13 +6,15 @@ title: Two-Party Buyer Protocol var Syndicate = require('../../src/main.js'); --> +# {{ page.title }} + This is an extended two-buyer book-purchase protocol, based loosely on an example given in: > K. Honda, N. Yoshida, and M. Carbone, “Multiparty asynchronous > session types,” POPL 2008. -# The Scenario +## The Scenario A book-seller responds to requests for book prices when asked. A pair of prospective buyers run through a shopping list. For each @@ -22,9 +24,9 @@ otherwise, it rejects the offer, and the first buyer tries a different split. If the second buyer agrees to a split, it then negotiates the purchase of the book with the book-seller. -# The Protocol +## The Protocol -## Role: SELLER +### Role: SELLER - when interest in `bookQuote($title, _)` appears, asserts `bookQuote(title, Maybe Float)`, `false` meaning not available, @@ -33,17 +35,17 @@ negotiates the purchase of the book with the book-seller. asserts `order(title, offer-price, false, false)` for "no sale", otherwise `order(title, offer-price, PositiveInteger, String)`, an accepted sale. -## Role: BUYER +### Role: BUYER - observes `bookQuote(title, $price)` to learn prices. - observes `order(title, offer-price, $id, $delivery-date)` to make orders. -## Role: SPLIT-PROPOSER +### Role: SPLIT-PROPOSER - observes `splitProposal(title, asking-price, contribution, $accepted)` to make a split-proposal and learn whether it was accepted or not. -## Role: SPLIT-DISPOSER +### Role: SPLIT-DISPOSER - when interest in `splitProposal($title, $asking-price, $contribution, _)` appears, asserts `splitProposal(title, askingPrice, contribution, true)` @@ -52,7 +54,7 @@ negotiates the purchase of the book with the book-seller. `splitProposal(title, asking-price, contribution, false)` to indicate they are unwilling to go through with the deal. -# A Sample Run +## A Sample Run Run the program with `../../bin/syndicatec index.js | node` from a checkout of the Syndicate repository. @@ -94,9 +96,9 @@ checkout of the Syndicate repository. A has bought everything they wanted! The order for The Wind in the Willows has id 10001485, and will be delivered on March 9th -# The Code +## The Code -## Type Declarations +### Type Declarations First, we declare *assertion types* for our protocol. @@ -105,7 +107,7 @@ First, we declare *assertion types* for our protocol. assertion type splitProposal(title, price, contribution, accepted); -## Utilities +### Utilities This routine is under consideration for possible addition to the core library. @@ -118,7 +120,7 @@ core library. } } -## Implementation: SELLER +### Implementation: SELLER function seller() { actor { @@ -134,26 +136,36 @@ tracking the next order ID to be allocated. }; this.nextOrderId = 10001483; +Looking up a price yields `false` if no such book is in our +inventory. + + this.priceOf = function (title) { + return (title in this.books) && this.books[title]; + }; + The seller responds to interest in bookQuotes by asserting a responsive record, if one exists. react { during Syndicate.observe(bookQuote($title, _)) { - assert bookQuote(title, title in this.books ? this.books[title] : false); + assert bookQuote(title, this.priceOf(title)); } } It also responds to order requests. react { - on asserted Syndicate.observe(order($title, $offerPrice, _, _)) { + on asserted + Syndicate.observe(order($title, $offerPrice, _, _)) + { We cannot sell a book we do not have, and we will not sell for less than our asking price. - var askingPrice = title in this.books ? this.books[title] : false; + var askingPrice = this.priceOf(title); if ((askingPrice === false) || (offerPrice < askingPrice)) { - whileRelevantAssert(order(title, offerPrice, false, false)); + whileRelevantAssert( + order(title, offerPrice, false, false)); } else { But if we can sell it, we do so by allocating an order ID and @@ -163,7 +175,8 @@ replying to the orderer. delete this.books[title]; actor { - whileRelevantAssert(order(title, offerPrice, orderId, "March 9th")); + whileRelevantAssert( + order(title, offerPrice, orderId, "March 9th")); } } } @@ -171,7 +184,7 @@ replying to the orderer. } } -## Implementation: SPLIT-PROPOSER and book-quote-requestor +### Implementation: SPLIT-PROPOSER and book-quote-requestor function buyerA() { actor { @@ -207,7 +220,8 @@ First, retrieve a quote for the title, and analyze the result. console.log("A learns that "+title+" is out-of-stock."); buyBooks(); } else { - console.log("A learns that the price of "+title+" is "+price); + console.log("A learns that the price of "+title+ + " is "+price); Next, repeatedly make split offers to a SPLIT-DISPOSER until either one is accepted, or the contribution from the @@ -237,14 +251,22 @@ perform the BUYER role here. Make our proposal, and wait for a response. react until { - case asserted splitProposal(title, price, contribution, true) { - console.log("A learns that the split-proposal for "+title+" was accepted"); + case asserted + splitProposal(title, price, contribution, true) + { + console.log("A learns that the split-proposal for "+ + title+" was accepted"); buyBooks(); } - case asserted splitProposal(title, price, contribution, false) { - console.log("A learns that the split-proposal for "+title+" was rejected"); - trySplit(title, price, contribution + ((price - contribution) / 2)); + case asserted + splitProposal(title, price, contribution, false) + { + console.log("A learns that the split-proposal for "+ + title+" was rejected"); + trySplit(title, + price, + contribution + ((price - contribution) / 2)); } } } @@ -252,7 +274,7 @@ Make our proposal, and wait for a response. } } -## Implementation: SPLIT-DISPOSER and BUYER +### Implementation: SPLIT-DISPOSER and BUYER function buyerB() { actor { @@ -266,16 +288,23 @@ It spends its time waiting for a SPLIT-PROPOSER to offer a `splitProposal`. react { - on asserted Syndicate.observe(splitProposal($title, $price, $theirContribution, _)) { + on asserted + Syndicate.observe(splitProposal($title, + $price, + $theirContribution, + _)) + { var myContribution = price - theirContribution; - console.log("B is being asked to contribute "+myContribution+" toward "+title+ - " at price "+price); + console.log("B is being asked to contribute "+myContribution+ + " toward "+title+" at price "+price); We may not be able to afford contributing this much. if (myContribution > this.funds) { - console.log("B hasn't enough funds ("+this.funds+" remaining)"); - whileRelevantAssert(splitProposal(title, price, theirContribution, false)); + console.log("B hasn't enough funds ("+this.funds+ + " remaining)"); + whileRelevantAssert( + splitProposal(title, price, theirContribution, false)); } else { But if we *can* afford it, update our remaining funds and spawn a @@ -283,7 +312,8 @@ small actor to handle the actual purchase now that we have agreed on a split. var remainingFunds = this.funds - myContribution; - console.log("B accepts the offer, leaving them with "+remainingFunds+" remaining funds"); + console.log("B accepts the offer, leaving them with "+ + remainingFunds+" remaining funds"); this.funds = remainingFunds; actor { @@ -292,7 +322,10 @@ on a split. While waiting for order confirmation, take the opportunity to signal to our SPLIT-PROPOSER that we accepted their proposal. - assert splitProposal(title, price, theirContribution, true); + assert splitProposal(title, + price, + theirContribution, + true); When order confirmation arrives, this purchase is completed. @@ -309,7 +342,7 @@ When order confirmation arrives, this purchase is completed. } } -## Starting Configuration +### Starting Configuration ground dataspace { seller();