Skip to content

Instantly share code, notes, and snippets.

@yuanchuan
Last active January 12, 2018 15:14
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save yuanchuan/c30f5c657f08115aee22ce3613fee4c4 to your computer and use it in GitHub Desktop.
Save yuanchuan/c30f5c657f08115aee22ce3613fee4c4 to your computer and use it in GitHub Desktop.
Erlang-like pattern matching in JavaScript
const MARK = {
symbol: {
name: '__mark__',
type: 'condition',
},
add(fn) {
let { name, type } = this.symbol;
let addon = { [name]: type };
return Object.assign((...args) => fn(...args), addon);
},
check(fn) {
if (!fn) return false;
let { name, type } = this.symbol;
return fn[name] == type;
}
};
function toGroup(defs) {
return defs.reduce((group, def, i) => {
let next = defs[i + 1];
MARK.check(def) || group.push({
def: def,
cond: MARK.check(next) ? next : null
});
return group;
}, []);
}
function getArgTypeList(def) {
let reg = /\((.+)\) (=>|\{)/;
return ((String(def).match(reg) || [])[1] || '')
.split(/, /)
.map(n => {
switch (n) {
case '[]': return { name: '[]' }
case '_': return { name: 'any' }
default: return { name: 'normal' }
}
});
}
function compareArgs(args, typeList) {
for (let i = 0; i < typeList.length; ++i) {
let type = typeList[i], arg = args[i];
if (type.name == '[]') {
return (JSON.stringify(arg) == type.name);
}
return true;
}
}
function when(cond) {
if (typeof cond !== 'function') {
cond = () => false;
}
return MARK.add((...args) => cond(...args));
}
function define(...defs) {
let groups = toGroup(defs);
return (...args) => {
let argsLength = args.length;
for (let i = 0; i < groups.length; ++i) {
let { def, cond } = groups[i];
if (argsLength != def.length) continue;
let isTypeMatched = compareArgs(args, getArgTypeList(def));
if ((!cond && isTypeMatched)
|| (cond && cond(...args) && isTypeMatched)) {
return def(...args);
}
}
}
}
const fib = define(
N => 0, when(N => N == 0),
N => 1, when(N => N < 2),
N => fib(N - 1) + fib(N - 2)
);
// 6765
console.log( fib(20) );
const filter = define(
(L, F) => filter(L, F, []),
([], _, L1) => L1,
([H, ...T], F, L1) => filter(T, F, F(H) ? [...L1, H] : L1)
);
// [ 1, 3, 5, 7, 9 ]
console.log(
filter([1, 2, 3, 4, 5, 6, 7, 8, 9, 10], x => x % 2)
);
const foldl = define(
([], _, Acc) => Acc,
([H, ...T], F, Acc) => foldl(T, F, F(Acc, H))
);
const plus = (a, b) => a + b;
const multiply = (a, b) => a * b;
// 10
foldl([1, 2, 3, 4], plus, 0)
// 24
foldl([1, 2, 3, 4], multiply, 1)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment