TSMatcher
What is it?
It is a small library which improves switch
statement from JavaScript/TypeScript.
Why?
I am spoiled by advanced match
in Scala which can match deeply, on more cases at once or use guards.
This library strives to improve very basic switch
statement, closing the gap a bit between TypeScript and Scala (and other languages with powerful matching, like Haskell).
Show me code!
Let's implement a basic calculator:
; ; // using TSMatcher library .case'+',a + b .case'-',a - b .case'*',a * b .case'/',a / b .execMap; // using plain old switch statement; compute1, 2, '+'; // {a: 1, b: 2, op: '+', result: 3}
You can see that in many cases TSMatcher is more concise, yet more powerful, than the built-in switch
.
Example above mainly demonstrates an ability to use Matcher
as an expression which is very common in functional languages.
For more information read the features section.
Installation
You can use npm
npm i -S ts-matcher
or grab a compiled version from this repository in /dist/src
directory.
Basic usage
Create a matcher similarly to how one writes a switch
:
;Matchanimal
then add cases:
.case'spider',console.log'I don\'t like those.' .case'dog',console.log'What a good boy!'
and finally, don't forget to execute the matcher:
.exec;
You should see a result of out little program printed out:
What a good boy!
If no case is successful an exception is thrown.
Usually we use default
to handle unmatched values.
Match2 .case0,0 .case1,1 .default9 .exec; // 9
The animal example could be further simplified by using execMap
which allows us to do side-effects with the result (or to apply transformations):
Matchanimal .case'spider',`I don't like those.` .case'dog','What a good boy!' .execMapconsole.logx;
Features
For more complete examples of usage please look at tests.
Short-circuit evaluation of cases
Only first successfully matched case will get evaluated.
Matchtrue .casetrue,console.log0 .casetrue,console.log1 .exec; // only prints "0"
Deep equality
By default an equality check of the case
is deep.
;Matchobj .case,'a' .case,'b' .exec; // 'b'
Guards
You can also "match" against a function. This usage is very close to multiple if
statements chained by else
es.
Nice thing is that you can mix classic case
with conditional one caseGuarded
.
Match-5 .caseGuardedx < 0,'less' .case0,'zero' .caseGuardedx > 0,'more' .exec; // 'less'
Comparison to multiple values
In some languages, like Scala, one can have multiple values in one case
.
With TSMatcher you can compare to multiple values too:
Match'c' .caseMulti,2 .caseMulti,1 .exec; // 1
Processing result
To process (aka map
) a result you can use the execMap
chain-terminating method instead of exec
.
Match1 .case0,'bb' .default'ccc' .execMapx.length; // 3
From case handler is returned 'ccc'
, then a function in execMap
is called (passing it the 'ccc'
) and its result 3
is returned.
Equality checking
If package lodash.isequalwith
is present, then this function is used.
Otherwise ===
operator will be utilized.
customizer
can be passed to case
and caseMulti
to customize behaviour of equality checking.
You can change equality checking like this:
; // not well typed;EqualityChecker.initializecustomEqualityFunction;Match1 .case3,'1=3', 2 .exec; // '1=3'
Development
Installing Dependencies
yarn
Running Tests
yarn test
Building
yarn build
Output is located in dist/src
directory.
Drawbacks
Performance
As it is with majority of abstractions, it comes with a performance cost. If you require extremely high performance and/or are not willing to make a trade-off for better abstractions, then I don't recommend using this library. Please note that unless you plan on using it in a very tight loop (e.g. real-time rendering, computing animation in every frame, game loop or intensive data processing) then you are probably fine, since even with only 1ms of work inside switch/matcher impact of this library is for practical purposes non-existent (exactly same ops/s).
You can try yourself:
yarn run perf
The library could be improved to support "prepared" matcher objects, but at this time I have no need for it (I primarily write code for ordinary front-ends and this kind of performance is rarely needed).
Loss of type narrowing
If you rely in all your switch
es on type narrowing (tagged unions), then this library is not for you.
I might look into it in future, but I am not sure if it is even possible.