This code is a fork of the FGRibreau's work (https://github.com/FGRibreau/match-when) The main objective is to have some fun with ECMAScript6 (and to discover it).
I like this FGRibreau's idea and I try to give it some of my tastes on the interface and implementation. For the interface I'm not a big fan of the imbricated objects / array. In the implementation, I don't find all the serialization/deserialization stuff and the increment variable very sexy.
My idea is to use the conditional conditions (|| and &&) and to take benefit of the lazy execution.
The setup is pretty simple, simply require the library with match and you are ready to go!
const match = require('match-when').match;or globally
require('match-when/register'); // `match` is now globally availableNow let's see how we would write a factorial function:
const fact = match(
p => p.with
|| p(0) && 1
|| p() && ((n) => n * fact(n-1))
);
fact(10); // 3628800Clear and simple right?
Alternatively, match(<input>, patternSpecification) can be used to instantly perform a match:
function fact(n){
return match(n,
p => p.with
|| p(0) && 1
|| p() && ((n) => n * fact(n-1))
);
}
fact(10); // 3628800Note that p() is a catch-all pattern and, if used, should always be the last condition. If you forget it match() will throw a MissingCatchAllPattern exception if nothing was matched.
todo
match works well with high order functions like map, filter (and so on) too:
[2, 4, 1, 2].map(match(
p => p.with
|| p(1) && "one"
|| p(2) && "two"
|| p() && "many"
));
// [ 'two', 'many', 'one', 'two' ]It also works with arrays:
function length(list){
return match(
p => p.with
|| p([]) && (() => 0)
|| p(p.head, p.tail) && ((head, tail) => 1 + length(tail))
)(list);
}
length([1, 1, 1]); // 3Note: because we use conditional operators, the right member of a matching line can not return a value which can be evaluated to false (false, 0, ...). So the use of a function is a workaround.
Sadly JavaScript does not offer us a way to overload operators so we're stuck with p.or:
function parseArgument(arg){
return match(
p => p.with
|| p.or("-h", "--help") && (() => displayHelp)
|| p.or("-v", "--version") && (() => displayVersion)
|| p() && (whatever => unknownArgument.bind(null, whatever))
)(arg);
}
parseArgument(process.argv.slice(1)); // displayHelp || displayVersion || (binded)unknownArgumentconst output = input.map(match(
p => p.with
|| p.and({protocol:'AMQP'}, {i:5}) && (o => o.i)
|| p.and({protocol:'HTTP'}, {i:10}) && (o => o.i)
|| p() && (o => 0)
));match-when supports regular expressions as well:
['hey.com', 'fg@plop.com', 'fg+plop@plop.com', 'wat']
.filter(match(
p => p.with
|| p(/\S+@\S+\.\S+/) && (o => false) // **seems** to be a valid email
|| p() && true // the email may be invalid, return it
));
// ['hey.com', 'wat'][12, 42, 99, 101].map(match(
p => p.with
|| p.range(0, 41) && '< answer'
|| p.range(43, 100) && '> answer'
|| p(42) && 'answer'
|| p() && '< 0, or > 100'
));
// ['< answer', 'answer', '> answer', '< 0, or > 100']{ x1: pattern1, ..., xn: patternn }- matches any object with property namesx1toxnmatching patternspattern1topatternn, respectively. Only the own properties of the pattern are used.[pattern0, ..., patternn]- matches any object with property names 0 to n matching patternspattern0topatternn, respectively./pattern/flags- matches any values than pass the regular expression testp.range(low, high)matches any number value in the range [low, high],lowandhighincluded.p.or(pattern0, ..., patternn)- matches if at least onepatternmatches.p.and(pattern0, ..., patternn)- matches if everypatternmatches.
- discover an alternative to the && separator that I don't like very much (not very expressive)
I work at iAdvize. iAdvize is the leading real-time customer engagement platform in Europe and is used in 40 different countries. We are one of the french startup with the fastest growth and one of the greatest place to work in France.
We are looking for a NodeJS backend developer, a Scala backend developer, a JavaScript frontend developer, a Full-stack Developer and a DevOps System Engineer in Paris or Nantes. Send a tweet to FGRibreau if you have any questions!
