Skip to content

Instantly share code, notes, and snippets.

@luciferous
Created September 29, 2012 13:11
Show Gist options
  • Save luciferous/3803955 to your computer and use it in GitHub Desktop.
Save luciferous/3803955 to your computer and use it in GitHub Desktop.
Do-notation in Javascript

do.js

An experimental Javascript library that can be used for asynchronous control flow, non-determinism, parsing, and other kinds of computations which implement the interface:

bind :: m a -> (a -> m b) -> m b
return :: a -> m a

This gist is a temporary living situation for do.js, and will most likely be expanded into a full blown Github repository in the future.

Examples

Happy experimenting!

var Y = function(f) {
return
(function (x) { return f(function (v) { return x(x)(v); }); })
(function (x) { return f(function (v) { return x(x)(v); }); });
}
var dot = function(f, g) {
return function(x) {
return f(g(x));
};
};
var thunkEval = function(z) {
while (z.constructor === Array) z = z[0].apply(null, z.slice(1));
return z;
};
var foldl = function(f, z0, xs0) {
var go = function(z, xs) {
if (xs.length === 0) return z;
return [go, f(z, xs[0]), xs.slice(1)];
}
return thunkEval(go(z0, xs0));
};
var papply = function(f) {
var args = [].slice.call(arguments, 1);
return function() {
return f.apply(this, args.concat([].slice.call(arguments)));
};
};
var cofoldl = function(f, z0) {
var go = function(z1) {
var z2 = f(z0, z1);
return cofoldl(f, z2);
};
return [z0, go];
};
var cohelper = function(q) {
return function(x1) {
return (arguments.length === 0) ?
q[0] : cohelper(q[1]([].slice.call(arguments)));
};
};
var foldM = function(f, a, as) {
if (as.length === 0) return a;
return List.bind(f(a, as[0]), function(fax) {
return foldM(f, fax, as.slice(1));
});
};
var add = function(a, b) { return a + b; };
var sum = papply(foldl, add, 0);
var shift = function(o) {
for (var k in o) return [k, o[k]];
};
var Monad = function(m, defs) {
for (var k in defs) {
m[k] = defs[k];
}
var ctx = {}; // TODO Totally the wrong place for this.
var prepare = function(args) {
args = args.constructor === Array ? args : [args];
if (args[0].constructor === Object) {
var _ = shift(args[0]), name = _[0], func = _[1];
func = defs.bind(func, function(x) {
ctx[name] = x;
return defs.return_();
});
args[0] = func;
}
var sargs = args.slice(1);
for (var i = 0; i < sargs.length; i++) {
if (sargs[i] in ctx) {
sargs[i] = ctx[sargs[i]];
}
}
return sargs.length === 0 ? args[0] : args[0].apply(null, sargs);
};
m.do_ = function(x) {
return cohelper(cofoldl(function(m0, m1) {
return defs.bind(prepare(m0), function(_) { return prepare(m1); });
}, x));
};
m.liftM = function(f) {
return function(m) {
return defs.bind(m, function(x) {
return defs.return(f(x));
});
};
};
};
var Cont = function() {};
Monad(Cont, {
bind: function(m, f) {
return function(k) {
return m(function(x) {
// TODO trampoline
setTimeout(function() {
var next = f(x);
if (next) next(k);
}, 0);
});
};
},
return_: function(x) {
return function(k) {
return k(x);
};
}
});
var forever = function(p, m) {
var m_ = Cont.bind(m, function(_) { return m_; });
return m_;
};
var List = function() {};
Monad(List, {
bind: function(m, f) {
var xs = [];
for (var i = 0; i < m.length; i++) {
xs = xs.concat(f(m[i]));
}
return xs;
},
return_: function(x) { return [x]; },
mplus: function(a, b) { return a.concat(b); },
mzero: []
});
if (typeof exports === "undefined") exports = window;
exports.dot = dot;
exports.sum = sum;
exports.foldl = foldl;
exports.Cont = Cont;
exports.T = T;
exports.Monad = Monad;
exports.cofoldl = cofoldl;
exports.cohelper = cohelper;
exports.forever = forever;
exports.List = List;
var mousedown = function(el) {
return function(k) {
var handle = function handle(e) {
el.removeEventListener("mousedown", handle, false);
k(e);
};
el.addEventListener("mousedown", handle, false);
return Cont.return_();
};
};
var update = function(e, downE) {
var x = e.clientX - (downE.offsetX || downE.layerX),
y = e.clientY - (downE.offsetY || downE.layerY);
downE.target.style.left = x + "px";
downE.target.style.top = y + "px";
};
var register = function(k) {
var handler = function(e) {
k([e, handler]);
};
window.addEventListener("mousemove", handler, false);
window.addEventListener("mouseup", handler, false);
};
var moveOrCancel = function(e, downE) {
if (e[0].type == "mousemove") {
update(e[0], downE);
} else {
window.removeEventListener("mousemove", e[1], false);
window.removeEventListener("mouseup", e[1], false);
return Cont.return_();
}
};
var dragdrop = function(el) {
var d =
(Cont.do_)
({ downE: mousedown(el) })
({ e: register })
(moveOrCancel, "e", "downE")
();
return forever(Cont, d);
};
var el = document.createElement("div");
el.style.position = "absolute";
el.style.background = "white";
el.style.border = "1px solid black";
el.style.height = "50px";
el.style.width = "50px";
document.body.appendChild(el);
dragdrop(el)(function() {});​
var dojs = require("do.js");
var Cont = dojs.Cont;
var Monad = dojs.Monad;
var List = dojs.List;
var foldM = dojs.foldM;
var T = function(type, value) { return { type: type, _: value }; };
var parseHexDigit = function(n, c) {
if (n.type == "Hex") {
if (/[a-fA-F\d]/.test(c)) {
n._ = n._ * 16 + parseInt(c, 16);
return List.return_(n);
}
}
return List.mzero;
}
var parseDigit = function(n, c) {
if (n.type == "Digit") {
if (/\d/.test(c)) {
n._ = n._ * 10 + parseInt(c);
return List.return_(n);
}
}
return List.mzero;
}
var parseWord = function(s, c) {
if (s.type == "Word") {
if (/[a-zA-Z]/.test(c)) {
s._ = s._ + c;
return List.return_(s);
}
}
return List.mzero;
}
var parse = function(p, c) {
var a = List.mplus(parseHexDigit(p, c), parseDigit(p, c));
a = List.mplus(a, parseWord(p,c));
return a;
}
var parseArg = function(s) {
var init = [ T("Hex", 0), T("Digit", 0), T("Word", "") ];
return List.bind(init, function(x) {
return foldM(parse, x, s.split(""));
});
};
console.log(parseArg("FFFF"));
@gunar
Copy link

gunar commented Oct 13, 2017

I can't find the "do-notation" in this.

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