Skip to content

Instantly share code, notes, and snippets.

@zerokarmaleft
Last active August 29, 2015 14:10
Show Gist options
  • Save zerokarmaleft/c87c5530aaa1a8cd6a4b to your computer and use it in GitHub Desktop.
Save zerokarmaleft/c87c5530aaa1a8cd6a4b to your computer and use it in GitHub Desktop.
core.async and js-csp side-by-side
(ns vending-machine.core
(:require [cljs.core.async
:refer [<! >! chan timeout]]
[figwheel.client :as figwheel
:include-macros [true]]
[om.core :as om
:include-macros true]
[sablono.core :as html
:refer-macros [html]])
(:require-macros [cljs.core.async.macros
:refer [go go-loop alt!]]))
(enable-console-print!)
;; (figwheel/watch-and-reload :websocket-url "ws://localhost:3449/figwheel-ws")
(defn customer [name coins chocolates toffees]
(go
(<! (timeout (* (Math/random) 60000)))
(println (str name ": inserting coin..."))
(>! coins 1)
(println (str name ": coin inserted!"))
(let [candies (if (> (Math/random) 0.5) chocolates toffees)]
(println (str name ": got a " (<! candies) ".")))))
(defn vending-machine [{:keys [name coins chocolates toffees]} owner]
(reify
om/IInitState
(init-state [_]
{:coins-inserted 0
:chocolates-taken 0
:toffees-taken 0})
om/IWillMount
(will-mount [this]
(go-loop []
(println "Waiting for customer to insert coin...")
(<! coins)
(println "Coin inserted!")
(om/update-state! owner :coins-inserted inc)
(println "Waiting for customer to take candy...")
(alt!
[[chocolates :chocolate]] (om/update-state! owner :chocolates-taken inc)
[[toffees :toffee]] (om/update-state! owner :toffees-taken inc))
(println "Candy taken!")
(recur)))
om/IRenderState
(render-state [_ {:keys [coins-inserted chocolates-taken toffees-taken]}]
(html
[:div
[:h2 (str "Vending Machine (" name ")")]
[:div {:id "status"}
[:h4 "Status"]
[:ul
[:li (str "Coins inserted: " coins-inserted)]
[:li (str "Chocolates taken: " chocolates-taken)]
[:li (str "Toffees taken: " toffees-taken)]]]]))))
(defn make-channels [n]
(take n (repeatedly chan)))
(defn vending-machine-grid [props owner]
(reify
om/IWillMount
(will-mount [this]
(let [coins (make-channels (:size props))
chocolates (make-channels (:size props))
toffees (make-channels (:size props))
names (map #(str "[Customer " % "]")
(range 1 (inc (* (:size props) (:num-customers props)))))
customers (->> (interleave names (cycle coins) (cycle chocolates) (cycle toffees))
(partition 4))]
(om/set-state! owner {:coins coins
:chocolates chocolates
:toffees toffees})
(doseq [[name coins chocolates toffees] customers]
(go
(println (str name ": getting in line."))
(customer name coins chocolates toffees)))))
om/IRenderState
(render-state [_ {:keys [coins chocolates toffees]}]
(html
[:table
[:thead]
[:tbody
(for [n (range (:size props))]
[:tr {:key (str "vm" n)}
[:td
(om/build vending-machine {:name (str "vm" n)
:coins (nth coins n)
:chocolates (nth chocolates n)
:toffees (nth toffees n)})]])]]))))
(om/root vending-machine-grid
{:size 4, :num-customers 2500}
{:target (.getElementById js/document "vending-machine")})
/**
* @jsx React.DOM
*/
var csp = require('csp');
var VendingMachine = React.createClass({
getInitialState: function() {
return {
coinsInserted: 0,
chocolatesDispensed: 0,
toffeesDispensed: 0
};
},
componentDidMount: function() {
csp.go(this.serveCustomers, [this.props.coins, this.props.chocolates, this.props.toffees]);
},
render: function() {
return (
<div>
<h2>Vending Machine ({this.props.name})</h2>
<div id='status'>
<h4>Status</h4>
<ul>
<li>Coins inserted: {this.state.coinsInserted}</li>
<li>Chocolates dispensed: {this.state.chocolatesDispensed}</li>
<li>Toffees dispensed: {this.state.toffeesDispensed}</li>
</ul>
</div>
</div>
);
},
serveCustomers: function*(coins, chocolates, toffees){
while (true) {
console.log("Waiting for customer to insert coin...");
yield csp.take(coins);
this.setState({ coinsInserted: this.state.coinsInserted + 1 });
console.log("Coin inserted!");
this.setState({ coinInserted: true });
console.log("Waiting for customer to retrieve candy...");
var candy = yield csp.alts([[chocolates, 'chocolate'], [toffees, 'toffee']]);
if (candy.channel === chocolates) {
this.setState({ chocolatesDispensed: this.state.chocolatesDispensed + 1 });
} else if (candy.channel === toffees) {
this.setState({ toffeesDispensed: this.state.toffeesDispensed + 1 });
}
console.log("Candy retrieved!");
this.setState({ coinInserted: false });
}
}
});
var VendingMachineGrid = React.createClass({
render: function() {
var gridSize = this.props.gridSize,
coins = this.props.coins,
chocolates = this.props.chocolates,
toffees = this.props.toffees;
var vms = _.chain(_.range(gridSize))
.map(function(vm, n) {
return (
<tr>
<td>
<VendingMachine name = {'vm' + n}
coins = {coins[n]}
chocolates = {chocolates[n]}
toffees = {toffees[n]} />
</td>
</tr>
);
});
return (
<table>
<thead>
</thead>
<tbody>
{vms}
</tbody>
</table>
);
}
});
var VendingMachineSim = React.createClass({
componentDidMount: function() {
var coins = this.props.coins,
chocolates = this.props.chocolates,
toffees = this.props.toffees;
for (var i = 0; i < this.props.gridSize; i++) {
for (var j = 0; j < this.props.numCustomers; j++) {
var id = (i+1) * (j+1);
console.log('[Customer ' + id + ']: getting in line.');
csp.go(this.customer, ['[Customer ' + id + ']',
coins[i], chocolates[i], toffees[i]]);
}
}
},
render: function() {
return (
<VendingMachineGrid gridSize = {this.props.gridSize}
coins = {this.props.coins}
chocolates = {this.props.chocolates}
toffees = {this.props.toffees} />
);
},
customer: function*(name, coins, chocolates, toffees) {
// wait for a random amount of time
yield csp.take(csp.timeout(Math.random() * 60000));
// make a random candy selection
var candies = (Math.random() > 0.5) ? chocolates : toffees;
console.log(name + ': inserting coin...');
yield csp.put(coins, 1);
console.log(name + ': coin inserted!');
console.log(name + ': retrieving candy...');
var candy = yield csp.take(candies);
console.log(name + ': got a ' + candy + '.');
}
});
var gridSize = 4;
var numCustomers = 2500;
function makeChannels(n) {
var channels = [];
for (var i = 0; i < n; i++) {
channels.push(csp.chan());
}
return channels;
}
React.render(
<VendingMachineSim gridSize = {gridSize}
numCustomers = {numCustomers}
coins = {makeChannels(gridSize)}
chocolates = {makeChannels(gridSize)}
toffees = {makeChannels(gridSize)}
/>,
document.getElementById('vending-machine')
);
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment