Created
April 30, 2016 20:08
-
-
Save kevinbarabash/49bcc859faf5d6c590f6401131244a6c to your computer and use it in GitHub Desktop.
using operator overloading for pattern matching
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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