Skip to content

Instantly share code, notes, and snippets.

@kevinbarabash
Created April 30, 2016 20:08
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 kevinbarabash/49bcc859faf5d6c590f6401131244a6c to your computer and use it in GitHub Desktop.
Save kevinbarabash/49bcc859faf5d6c590f6401131244a6c to your computer and use it in GitHub Desktop.
using operator overloading for pattern matching
console.log('');
`Number | String`; // what about chaining?
`{ type: String }`;
`{ type: 'FunctionDeclaration' | 'FunctionExpression' }`;
// etc.
// what if... we provide definitions for combining two functions or two literals to provide pattern matching
const isMatcher = Symbol('isMatcher');
Function.defineOperator(
'|',
[Function, Function],
(a, b) => {
const matcher = (value) => {
let aMatch = false;
let bMatch = false;
if (a[isMatcher]) {
aMatch = a(value);
} else if (a === String) {
aMatch = typeof value === "string";
} else if (a === Number) {
aMatch = typeof value === "number";
} else {
aMatch = value instanceof a;
}
if (b[isMatcher]) {
bMatch = b(value);
} else if (b === String) {
bMatch = typeof value === "string";
} else if (b === Number) {
bMatch = typeof value === "number";
} else {
bMatch = value instanceof b;
}
return aMatch || bMatch;
};
matcher[isMatcher] = true;
return matcher;
}
);
Function.defineOperator(
'|',
[String, String],
(a, b) => {
const matcher = (value) => {
return a === value || b === value;
};
matcher[isMatcher] = true;
return matcher;
}
);
Function.defineOperator(
'|',
[String, Function],
(a, b) => {
const matcher = (value) => {
let bMatch = false;
if (b[isMatcher]) {
bMatch = b(value);
} else if (b === String) {
bMatch = typeof value === "string";
} else if (b === Number) {
bMatch = typeof value === "number";
} else {
bMatch = value instanceof b;
}
return a === value || bMatch;
};
matcher[isMatcher] = true;
return matcher;
}
);
Function.defineOperator(
'|',
[Number, Number],
(a, b) => {
const matcher = (value) => {
return a === value || b === value;
};
matcher[isMatcher] = true;
return matcher;
}
);
Function.defineOperator(
'|',
[Number, Function],
(a, b) => {
const matcher = (value) => {
let bMatch = false;
if (b[isMatcher]) {
bMatch = b(value);
} else if (b === String) {
bMatch = typeof value === "string";
} else if (b === Number) {
bMatch = typeof value === "number";
} else {
bMatch = value instanceof b;
}
return a === value || bMatch;
};
matcher[isMatcher] = true;
return matcher;
}
);
Function.defineOperator(
'|',
[Number, String],
(a, b) => {
const matcher = (value) => {
return a === value || b === value;
};
matcher[isMatcher] = true;
return matcher;
}
);
Function.defineOperator(
'|',
[RegExp, RegExp],
(a, b) => {
const matcher = (value) => {
return a.test(value) || b.test(value);
};
matcher[isMatcher] = true;
return matcher;
}
);
Symbol.any = Symbol('any');
// Any matcher
const _ = (value) => true;
// TODO: limit the scope of certain operators
// for example, I'd like to limit the scope of using | so that I can do things like
// { x: _, y: _ } | { p: _, q: _ }
console.log(`matcher = Number | Object | 'hello'`);
const matcher = Number | Object | 'hello';
console.log(`matcher({}) = ${matcher({})}`);
console.log(`matcher('hello') = ${matcher('hello')}`);
console.log(`matcher('goodbye') = ${matcher('goodbye')}`);
console.log(`matcher(123) = ${matcher(123)}`);
console.log(`matcher(true) = ${matcher(true)}`);
console.log('');
console.log(`helloOr123 = 'hello' | 123;`);
const helloOr123 = 'hello' | 123;
console.log(`helloOr123('hello') = ${helloOr123('hello')}`);
console.log(`helloOr123('goodbye') = ${helloOr123('goodbye')}`);
console.log(`helloOr123(123') = ${helloOr123(123)}`);
console.log(`helloOr123(-1') = ${helloOr123(-1)}`);
console.log('');
const any = _;
console.log(`any({ foo: 'bar' }) = ${any({ foo: 'bar' })}`);
console.log('');
const match = (value) => {
return (...args) => {
const lastIndex = args.length - 1;
const types = args.slice(0, lastIndex);
for (const type of types) {
if (type instanceof Function) {
// TODO check if value is an instance of type
// TODO if value is a function check if it has the same number of args
}
}
const callback = args[lastIndex];
}
};
match('hello',
['hello', 123, () => `'hello' or 123`],
[Number, String, () => 'number or string'],
[any, () => 'any other value']
);
// const match = function(pattern, value) {
// if (pattern instanceof )
// };
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment