sweet.js macros, so it looks and feels
like native syntax. It has no runtime dependencies and compiles down to simple
Here's a small slice of what you can do with it:
function myPatterns// Match literals42 => 'The meaning of life'// Tag checking for JS types using Object::toStringa @ String => 'Hello ' + a// Array destructuringfront back => backconcatfront// Object destructuringfoo: 'bar' x 'y' => x// Custom extractorsEmail user domain: 'foo.com' => user// Rest argumentsa b rest => rest// Rest patterns (mapping a pattern over many values)x y => _zipx y// Guardsx @ Number if x > 10 => x
You can see a slew of more examples in the pattern spec file
npm install -g sweet.js npm install sparkler sjs -m sparkler/macros myfile.js
Sparkler overloads the
function keyword as a macro (don't worry, all your old
functions will still work) but implements a slightly different syntax. There's
no argument list after the name or function keyword. Instead the function body
is just a set of ES6 style arrow-lambdas separated by commas.
function myFunc// Single arguments don't need parens and simple expression// bodies get an implicit `return`.a @ String => 'Hello ' + a// Add parens and braces if you need more.x y z =>return x + y + z;
You can also do this with anonymous functions:
DBgetResource'123' functionnull resp => completeresperr => handleErrorerr
If no case matches, a
TypeError('No match') is thrown.
Sparkler doesn't just try each case one at a time until one passes. That would be really inefficient. Instead, it analyzes your entire pattern matrix, and rearranges things as needed to get an optimized set of tests while still preserving the left-to-right, top-down semantics.
function expensiveExtractionMyExtractorx y z 1 => doThisMyExtractorx y z * => doThat
MyExtractor is really expensive. Sparkler efficiently backtracks,
so it will only get called once in this set of tests.
that are not provided are just set to
undefined. Sparkler does not implicitly
match on argument length.
function ambiguousa => 1a b => 2a b c => 3
The above function will always return
1 no matter how many arguments you
call it with as the first case always matches. The subsequent cases are
actually removed from the final output in the optimization phase.
If you want to match on specific argument length, you need to add a guard to your case.
function argCheck// Using arguments.lengtha b c if argumentslength == 3 => 1// Or matching undefineda b undefined => 2a => 1
The only time Sparkler is strict with argument length is with the empty
(). It will check that
arguments.length is zero. This is so
you can do stuff like this:
// jQuery-style getter/setterval: function=> this_valval =>this_val = val;return this;
If you want a catch-all, you should use a wildcard (
Sparkler exports a
match macro for doing easy matching in any position.
Match expressions look like
function matching, except you provide the
var num = 12;var isNumber = match numNumber => true* => false;
This works by desugaring
match into a self-invoking function with
the argument. Consequently,
match expressions do not support
continue, and early
return. Using a
match expression in statement
position will result in a parse error.
Match statements use a slightly different syntax. They look like a suped up
var a = FooFooFoo42;while 1match acase Fooinner:a = inner;default:break;
cases in a
match statement do not fall through. Early
switch are all supported. Using a
match statement in
expression position will result in a parse error.
You can match on multiple expressions at once in both
match expressions and
var allNums = match num1 num2 num3Number Number Number => true* => false;match num1 num2 num3case Number Number Number:allNums = true;default:allNums = false;
All bindings in patterns are declared as
vars by default, as it is the most
widely supported declaration form. Consequently, they will hoist outside of
match statements. You may specify your declaration form by prefixing pattern
bindings with one of
match xcase Fooa: // will hoistcase Foovar a: // will hoistcase Foolet a: // will not hoistcase Fooconst a: // will not hoist, immutable
You can match on your own types by implementing a simple protocol. Let's build a simple extractor that parses emails from strings:
var Email =// Factor out a matching function that we'll reuse.match: functionx @ String => xmatch/@/* => null// `hasInstance` is called on bare extractors.return !!Emailmatchx;// `unapply` is called for array-like destructuring.var m = Emailmatchx;if mreturn m1 m2;// `unapplyObject` is for object-like destructuring.var m = Emailmatchx;if mreturnuser: m1domain: m2;;
Now we can use it in case arguments:
function doStuffWithEmails// Calls `unapplyObject`Email domain: 'foo.com' =>// Calls 'unapply'Email'foo' * =>// Calls `hasInstance`Email =>
If you don't implement
hasInstance, Sparkler will fall back to a simple
adt-simple is a library that implements the extractor protocol out of the box, and even has its own set of macros for defining data-types.
var adt = require'adt-simple';union TreeEmptyNodevalue : *left : Treeright : Treederiving adtExtractorfunction treeFnEmpty => 'empty'Node value @ String => 'string'
Sparkler also provides a small library that extends some of the native types with convenient functions.
require'sparkler/extend';// Date destructuringfunction dateStuffDate month year =>// RegExp destructuringfunction regexpStuffRegExp flags: 'i' =>// Partial-function composition with `orElse`function partialFoo => 'foo'var total = partialorElsefunction* => 'anything'
orElse is added to the prototype safely using
and isn't enumerable.
Nathan Faubion (@natefaubion)