Skip to content

Instantly share code, notes, and snippets.

@nowelium
Created March 27, 2011 15:01
Show Gist options
  • Star 2 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save nowelium/889276 to your computer and use it in GitHub Desktop.
Save nowelium/889276 to your computer and use it in GitHub Desktop.
javascript Actor: scala's actor style
Function.prototype.bind = function(obj){
var __method__ = this;
var __args__ = Array.prototype.slice.call(arguments);
__args__.shift(); // obj
return function (){
var args = Array.prototype.slice.call(arguments);
return __method__.apply(obj, __args__.concat(args));
};
};
Function.prototype.curry = function (){
var __method__ = this;
var __args__ = Array.prototype.slice.call(arguments);
return function (){
var args = Array.prototype.slice.call(arguments);
return __method__.apply(this, __args__.concat(args));
};
};
var GlobalActor = new function (){
var call = function(callback){
setTimeout(callback, 0);
};
var Queue = function (){};
Queue.prototype.enqueue = function (callback){
call(callback);
};
var AbstractActor = function(){
this.queue = new Queue();
};
AbstractActor.prototype.act = function (){
throw new Error('override actor');
};
AbstractActor.prototype.start = function (){
this.queue.enqueue((function (){
this.act.call(this, this);
}).bind(this));
};
AbstractActor.prototype.stop = function (){
this.queue.enqueue = function (){
// nop
};
};
this.AbstractActor = AbstractActor;
};
var Actor = function (){
this.messages = [];
};
Actor.MESSAGE_BANG = 1;
Actor.MESSAGE_BANGBANG = 2;
Actor.MESSAGE_BANGQMARK = 3;
Actor.prototype = new GlobalActor.AbstractActor();
Actor.prototype.loop = function(callback){
var self = this;
var loop = function (){
try {
callback.apply(self);
self.queue.enqueue(loop);
} catch(e) {
console.error(e);
}
};
self.queue.enqueue(loop);
};
Actor.prototype.performMessage = function(callback){
// clear reply
this.reply = function (){};
// dequeue message
var message = this.messages.shift();
switch(message.type){
case Actor.MESSAGE_BANG:
// no reply
return callback.call(this, message.parameter, message.sender);
case Actor.MESSAGE_BANGBANG:
// reply async
this.reply = function(value){
var partialFunction = message.partialFunction;
if(!partialFunction){
partialFunction = function(k){return k};
}
var partialResult = partialFunction(value);
var future = message.future;
this.queue.enqueue(future.callback.curry(partialResult));
};
return callback.call(this, message.parameter, message.sender);
case Actor.MESSAGE_BANGQMARK:
throw new Error('not yet supported');
}
};
Actor.prototype.react = function(callback){
var self = this;
return function (){
if(0 < self.messages.length){
self.performMessage.apply(self, [callback]);
}
};
};
Actor.prototype.receive = function(callback){
throw new Error('not yet supported');
};
Actor.prototype['!'] = function (_parameter, _sender){
this.messages.push({
type: Actor.MESSAGE_BANG,
parameter: _parameter,
sender: _sender,
partialFunction: null,
future: null
});
};
Actor.prototype['!!'] = function(_parameter, _partialFunction, _sender){
var __future__ = function(callback){
arguments.callee.callback = callback;
};
__future__.callback = null;
this.messages.push({
type: Actor.MESSAGE_BANGBANG,
parameter: _parameter,
sender: _sender,
partialFunction: _partialFunction,
future: __future__
});
return __future__;
};
Actor.prototype['!?'] = function(parameter){
throw new Error('not yet supported');
};
//
// example::
//
var Foo = function (){};
Foo.prototype = new Actor();
Foo.prototype.act = function (actor){
actor.loop(actor.react(function (message){
console.log(message.msg);
actor.reply(message.msg);
}));
};
var foo = new Foo();
foo.start();
foo['!']({msg: 'msg1'});
var f = foo['!!']({msg: 'msg2'}, function (returnValue){
return returnValue + ', partial';
});
f(function(result){
console.log(result);
foo.stop();
});
//
// PingPong:
// http://www.scala-lang.org/node/54
//
var MSG_Start = 1;
var MSG_SendPing = 2;
var MSG_Pong = 3;
var MSG_Ping = 4;
var MSG_Stop = 5;
var Ping = function(count, pong) {
this.count = count;
this.pong = pong;
};
Ping.prototype = new Actor();
Ping.prototype.toString = function (){
return 'Ping[count: ' + this.count + ', pong: ' + this.pong + ']';
};
Ping.prototype.act = function(actor){
console.log('Ping initializing with count:' + this.count + ': ' + this.pong);
var pingsLeft = this.count;
actor.loop(actor.react(function(message, sender){
switch(message){
case MSG_Start:
console.log('Ping:start');
this.pong['!'](MSG_Ping, this);
pingsLeft = pingsLeft - 1;
break;
case MSG_SendPing:
this.pong['!'](MSG_Ping, this);
pingsLeft = pingsLeft - 1;
break;
case MSG_Pong:
if(0 === (pingsLeft % 1000)){
console.log('Ping pong from: ' + sender);
}
if(0 < pingsLeft){
this['!'](MSG_SendPing, this);
} else {
console.log('Ping Stop');
this.pong['!'](MSG_Stop, this);
this.stop();
}
break;
}
}));
};
var Pong = function (){};
Pong.prototype = new Actor();
Pong.prototype.toString = function (){
return '[Pong]';
};
Pong.prototype.act = function(actor){
var pongCount = 0;
actor.loop(actor.react(function(message, sender){
switch(message){
case MSG_Ping:
if(0 === (pongCount % 1000)){
console.log('Pong ping:' + pongCount + ' from ' + sender);
}
sender['!'](MSG_Pong, this);
pongCount = pongCount + 1;
break;
case MSG_Stop:
console.log('Pong Stop');
this.stop();
break;
}
}));
};
var pong = new Pong();
var ping = new Ping(100000, pong);
ping.start();
pong.start();
ping['!'](MSG_Start);
@nowelium
Copy link
Author

うーん。

@nowelium
Copy link
Author

scalaのpingpongまでは動くようになった。

@nowelium
Copy link
Author

nowelium commented Apr 2, 2011

sender は外から指定するようにしてみた
setInterval -> setTimeout に変更してみた

@nowelium
Copy link
Author

nowelium commented Apr 3, 2011

partialFunction は function(k){return K} 的なものをデフォルトに
GlobalActorをどうしようか迷う...。(不要?)
setTimeoutでいいのかどうかは、未だに考え中

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment