SlideShare a Scribd company logo
@FGRibreau
Implementing pattern-matching in JavaScript
@FGRibreau
Implementing pattern-matching in JavaScript
…or how to play with EcmaScript shortcoming
-1
Pattern matching ?!?
@FGRibreau
“In computer science, pattern matching is
the act of checking a given sequence of
tokens for the presence of the constituents
of some pattern.” — wikipedia
@FGRibreau
def toYesOrNo(choice: Int): String = choice match {
case 1 => "yes"
case 0 => "no"
case _ => "error"
}
def fact(n: Int): Int = n match {
case 0 => 1
case n => n * fact(n - 1)
}
Pattern Matching in Scala
@FGRibreau
let rec factorial = function
| 0 -> 1
| n -> n * factorial(n - 1);;
Pattern Matching in OCaml
0
context
@FGRibreau
Une syntaxe de pattern-matching en JS ?
Sortez l'artillerie lourde.
@FGRibreau
_.flatten(links).map(link => {
[{protocol: 'HTTP'}]: => 1,
[{protocol: 'AMQP'}]: => 2
});
It would it be awesome to use some pattern-matching there right?
@FGRibreau
SyntaxError: /Users/FG/www/iadvize-services-
orchestration-tools/src/api/src/repositoryManagers/
github.js: Unexpected token (185:32)
183 |
184 | _.flatten(links).forEach(link => {
> 185 | [{protocol: 'HTTP'}]: => 1,
| ^
186 | [{protocol: 'AMQP'}]: => 2
187 | });
188 |
... and of course it’s not JS valid syntax
1
goal
@FGRibreau
syntactically short
javascript-minded syntax
functionally-way
I wanted a pattern-matching library
2
syntax
@FGRibreau
links.map(link => {
[{protocol: 'HTTP'}]: => 1,
[{protocol: 'AMQP'}]: => 2
});
Ok, so this is not valid,
what precisely is not valid and
how can we make it valid?
@FGRibreau
links.map(link => {
[{protocol: 'HTTP'}]: () => 1,
[{protocol: ‘AMQP'}]: () => 2
});
links.map(link => {
[{protocol: 'HTTP'}]: 1,
[{protocol: ‘AMQP'}]: 2
});
links.map(link => {
[{protocol: 'HTTP'}]: => 1,
[{protocol: 'AMQP'}]: => 2
});
@FGRibreau
{
[{protocol: ‘HTTP'}]: 1,
[{protocol: ‘AMQP’}]: 2
}
The rest is syntactically valid
ES6 "computed property names"
{
[{protocol: ‘HTTP’}]: () => 1,
[{protocol: ‘AMQP’}]: () => 2
}
@FGRibreau
links.map(link => {})
[undefined, undefined]
links.map(link => {1})
[undefined, undefined]
links.map(link => {return 1})
[1,1]
links.map(link => 1)
[1,1]
Syntactically valid, semantically invalid
… but then I won't have my pattern matching.
@FGRibreau
BUT…
@FGRibreau
If I go from there…
_.flatten(links).map(link => {
[{protocol: 'HTTP'}]: => 1,
[{protocol: 'AMQP'}]: => 2
});
@FGRibreau
If I go from there…
_.flatten(links).map(link => {
[{protocol: 'HTTP'}]: => 1,
[{protocol: 'AMQP'}]: => 2
});
_.flatten(links).map(match({
[{protocol: 'HTTP'}]: 1,
[{protocol: 'AMQP'}]: 2
}));
…to there…
@FGRibreau
… then it’s syntactically correct!
_.flatten(links).map(match({
[{protocol: 'HTTP'}]: 1,
[{protocol: 'AMQP'}]: 2
}));
@FGRibreau
… then it’s syntactically correct!
_.flatten(links).map(match({
[{protocol: 'HTTP'}]: 1,
[{protocol: 'AMQP'}]: 2
}));
@FGRibreau
… would be great too !
const linkNumber = match(link,{
[{protocol: 'HTTP'}]: 1,
[{protocol: 'AMQP'}]: 2
});
3
Semantic
@FGRibreau
{
[{protocol: ‘HTTP'}]: 1,
[{protocol: ‘AMQP’}]: 2
}
ES6 "computed property names"
“The object initializer syntax also supports computed property names.
That allows you to put an expression in brackets [], that will be computed as
the property name.”
@FGRibreau
{
'[object Object]': 2
}
evaluates to
ES6 "computed property names"
{
[{protocol: 'HTTP'}]: 1,
[{protocol: 'AMQP'}]: 2
}
plz fix this
plz fix this
@FGRibreau
evaluates to
ES6 "computed property names"
{
[when({protocol: ‘HTTP’})]: 1,
[when({protocol: ‘AMQP'})]: 2,
[when()]: 0,
}
{
'{"protocol":"HTTP"}': 1,
'{"protocol":"AMQP"}': 2,
Symbol('match.pattern.catchAll'): 0
}
@FGRibreau
evaluates to
ES6 "computed property names"
{
[when({protocol: ‘HTTP’})]: 1,
[when({protocol: ‘AMQP'})]: 2,
[when()]: 0,
}
{
'{"protocol":"HTTP"}': 1,
'{"protocol":"AMQP"}': 2,
Symbol('match.pattern.catchAll'): 0
}
@FGRibreau
ES6 "computed property names"
{
[when({protocol: ‘HTTP’})]: 1,
[when({protocol: ‘AMQP'})]: 2,
[when()]: 0,
}
{
'{"protocol":"HTTP"}': 1,
'{"protocol":"AMQP"}': 2,
Symbol('match.pattern.catchAll'): 0
}
function when(props){
if(props === undefined){
return _catchAllSymbol;
}
return _JSON.stringify(props);
}
@FGRibreau
Order is lost by match’s object
.map(match({
[when.range(0, 43)]: 42,
[when(42)]: 72
}))
when.range(0,43) => '["Symbol(match.pattern.RANGE)",0,43]'
when(42) => '[42]'
JS objects are an unordered collection of properties
@FGRibreau
Fixing properties declaration
?
.map(match({
[when.range(0, 43)]: 42,
[when(42)]: 72
}))
.map(match(new Map([
[ when.range(0, 43), 42 ],
[ when(42), 72 ],
[ when(), 'never should be hit' ]
])))
NO!
@FGRibreau
Fixing properties declaration
?
.map(match({
[when.range(0, 43)]: 42,
[when(42)]: 72
}))
.map(match([
[ when.range(0, 43), 42 ],
[ when(42), 72 ],
[ when(), 'never should be hit' ]
]))
NO!
plz fix this
plz fix this
@FGRibreau
Fixing properties declaration
.map(match({
[when.range(0, 43)]: 42,
[when(42)]: 72
}))
- same callsite
- single thread
- sequential evaluation
@FGRibreau
Fixing properties declaration
.map(match({
[when.range(0, 43)]: 42,
[when(42)]: 72
}))
- same callsite
- single thread
- sequential evaluation
@FGRibreau
Fixing properties declaration
when.range(0,43) => '[0, "Symbol(match.pattern.RANGE)",0,43]'
when(42) => '[1, 42]'
… #problemSolved …
.map(match({
[when.range(0, 43)]: 42,
[when(42)]: 72
}))
- same callsite
- single thread
- sequential evaluation
@FGRibreau
How to get matched value?
const fact = match({
[when(0)]: 1,
[when()]: n * fact(n-1)
});
fact(10);
n is not defined
@FGRibreau
How to get matched value?
const fact = match({
[when(0)]: 1,
[when()]: n * fact(n-1)
});
fact(10);
simple, use a function:
const fact = match({
[when(0)]: 1,
[when()]: (n) => n * fact(n-1)
});
fact(10); // 3628800
@FGRibreau
How to get matched value?
const fact = match({
[when(0)]: 1,
[when()]: n * fact(n-1)
});
fact(10);
simple, use a function:
const fact = match({
[when(0)]: 1,
[when()]: (n) => n * fact(n-1)
});
fact(10); // 3628800
@FGRibreau
const input = [{protocol: 'HTTP', i:10}, {protocol: 'AMQP', i:11},
{protocol: 'WAT', i:3}];
const output = input.map(match({
[when({protocol:'HTTP'})]: (i) => () => 1,
[when({protocol:'AMQP'})]: (i) => () => 2,
[when()]: (i) => () => 0,
}));
output.map((f) => f()) // => [1, 2, 20]
But how do I yield functions?
4
implementation
@FGRibreau
function match(/* args... */){
const args = Array.from(arguments),
obj = args[args.length-1];
// pre-compute matchers
let matchers = [];
for(let key in obj){
matchers.push(when.unserialize(key, obj[key])); // e.g. {match:(mixed)=>boolean,result:mixed,position:number}
}
matchers.sort(function(a, b){
return a.position < b.position ? -1 : 1;
});
if(Object.getOwnPropertySymbols(obj).indexOf(_catchAllSymbol) !== -1){
matchers.push(when.unserialize(_catchAllSymbol, obj[_catchAllSymbol]));
}
const calculateResult = function(input){
const matched = matchers.find((matcher) => matcher.match(input));
if (!matched) {
throw new MissingCatchAllPattern();
}
return typeof matched.result === 'function' ? matched.result(input) : matched.result;
};
return args.length === 2 ? calculateResult(args[0]) : calculateResult;
}
@FGRibreau
when.unserialize = function(serializedKey, value){
if(serializedKey === _catchAllSymbol){
return {
match: _true, // same as: () => true
result: value,
position: Infinity
};
}
const {position, matcherConfiguration} = _unserialize(serializedKey);
return {
match: _match(matcherConfiguration),
result: value,
position: position
};
};
@FGRibreau
function _match(props){ // [{type}, …]
if(Array.isArray(props)){
if(props[0] === _patternORStr){ // _patternORStr = Symbol(‘OR’)
props.shift();
return function(input){
return props[0].some((prop) => _matching(prop, input));
};
}
if(props[0] === _patternANDStr){ // _patternANDStr = Symbol(‘AND’)
props.shift();
return function(input){
return props[0].every((prop) => _matching(prop, input));
};
}
if(props[0] === _patternRANGEStr){ // _patternRANGEStr = Symbol(‘RANGE’)
props.shift();
return function(input){
return props[0] <= input && input <= props[1];
};
}
}
function _matching(props, input){
// [...]
if(props instanceof RegExp){
return props.test(input);
}
if(typeof input === 'object'){
for(let prop in props){
if(input.hasOwnProperty(prop) && input[prop] !== props[prop]){
return false;
}
}
return true;
}
return props === input;
}
return (input) => _matching(props, input);
}
5
wrap up
@FGRibreau
@FGRibreau
6
Next step
@FGRibreau
function replaceInstanceId(elem) {
if (_.isPlainObject(elem)) {
return _.mapValues(elem, replaceInstanceId);
} else if (_.isArray(elem)) {
return _.map(elem, replaceInstanceId);
} else if (_.isString(elem) && _.includes(elem, 'instance_id')) {
return _.template(elem)({instance_id: conf.instance_id});
}
return elem;
}
Type Matching
const replaceInstanceId = match({
[when(Object)]:(elem) => _.mapValues(elem, replaceInstanceId),
[when(Array)]:(elem) => _.map(elem, replaceInstanceId),
[when.and(String, /instance_id/)]: (elem) => _.template(elem)({instance_id:
conf.instance_id}),
[when()]: _.identity
});
With type matching
@FGRibreau
const replaceInstanceId = match({
// extraction
[when({a:when._, b:when._, c:{e:when._})]:(a, b, e) => {a, b, e},
[when(Array)]:(elem) => _.map(elem, replaceInstanceId),
[when()]: _.identity
});
Pattern Extraction
@FGRibreau
@FGRibreau
François-Guillaume
RIBREAU
@FGRibreau
Tightly crafted developer oriented
online real-time monitoring and
administration service for Redis.
Join us
Frontend Dev - Backend Dev
Fullstack Dev - DevOps
#scala #nodejs #react #docker #xmpp

More Related Content

What's hot (20)

PDF
Free your lambdas
José Paumard
 
PDF
AST Rewriting Using recast and esprima
Stephen Vance
 
PPTX
AST - the only true tool for building JavaScript
Ingvar Stepanyan
 
PPTX
Don't Be Afraid of Abstract Syntax Trees
Jamund Ferguson
 
PDF
JavaScript on the GPU
Jarred Nicholls
 
PDF
C++ Programs
NarayanlalMenariya
 
PDF
Lambda and Stream Master class - part 1
José Paumard
 
PDF
Introduction into ES6 JavaScript.
boyney123
 
PPT
Argon walkthru 1-26
Nachiketas Ramanujam
 
ODP
Java Boilerplate Busters
HamletDRC
 
PDF
Java 8 Streams & Collectors : the Leuven edition
José Paumard
 
ODP
ES6 PPT FOR 2016
Manoj Kumar
 
PDF
Virtual Machine Constructions for Dummies
National Cheng Kung University
 
PDF
JDK8 : parallel programming made (too ?) easy
José Paumard
 
PDF
Being functional in PHP (DPC 2016)
David de Boer
 
ODP
Exploiting the newer perl to improve your plugins
Marian Marinov
 
PDF
From Zero to Application Delivery with NixOS
Susan Potter
 
PDF
What's New in ES6 for Web Devs
Rami Sayar
 
PDF
Java Full Throttle
José Paumard
 
PDF
JavaScript Functions
Colin DeCarlo
 
Free your lambdas
José Paumard
 
AST Rewriting Using recast and esprima
Stephen Vance
 
AST - the only true tool for building JavaScript
Ingvar Stepanyan
 
Don't Be Afraid of Abstract Syntax Trees
Jamund Ferguson
 
JavaScript on the GPU
Jarred Nicholls
 
C++ Programs
NarayanlalMenariya
 
Lambda and Stream Master class - part 1
José Paumard
 
Introduction into ES6 JavaScript.
boyney123
 
Argon walkthru 1-26
Nachiketas Ramanujam
 
Java Boilerplate Busters
HamletDRC
 
Java 8 Streams & Collectors : the Leuven edition
José Paumard
 
ES6 PPT FOR 2016
Manoj Kumar
 
Virtual Machine Constructions for Dummies
National Cheng Kung University
 
JDK8 : parallel programming made (too ?) easy
José Paumard
 
Being functional in PHP (DPC 2016)
David de Boer
 
Exploiting the newer perl to improve your plugins
Marian Marinov
 
From Zero to Application Delivery with NixOS
Susan Potter
 
What's New in ES6 for Web Devs
Rami Sayar
 
Java Full Throttle
José Paumard
 
JavaScript Functions
Colin DeCarlo
 

Similar to Implementing pattern-matching in JavaScript (full version) (20)

PDF
Modern Application Foundations: Underscore and Twitter Bootstrap
Howard Lewis Ship
 
PPTX
Academy PRO: ES2015
Binary Studio
 
PDF
Extensible Operators and Literals for JavaScript
Brendan Eich
 
PDF
Workshop 10: ECMAScript 6
Visual Engineering
 
PDF
Web futures
Brendan Eich
 
PDF
Introduction to ECMAScript 2015
Tomasz Dziuda
 
PDF
Elm: give it a try
Eugene Zharkov
 
PDF
Reason - introduction to language and its ecosystem | Łukasz Strączyński
Grand Parade Poland
 
PDF
Coscup2021 - useful abstractions at rust and it's practical usage
Wayne Tsai
 
PDF
Practical JavaScript Programming - Session 7/8
Wilson Su
 
PDF
ECMAScript 6 major changes
hayato
 
PPTX
Workshop 1: Good practices in JavaScript
Visual Engineering
 
PDF
Value Objects, Full Throttle (to be updated for spring TC39 meetings)
Brendan Eich
 
PDF
ECMAScript 6
WebF
 
PPT
Functional Patterns for the non-mathematician
Brian Lonsdorf
 
PDF
ECMAScript 6 new features
GephenSG
 
PDF
Configurable Pattern Matching Semantics in openCypher: Defining Levels of Nod...
openCypher
 
PPTX
ECMA5 and ES6 Promises
Oswald Campesato
 
PDF
ECMAScript2015
qmmr
 
PPTX
Data weave 2.0 advanced (recursion, pattern matching)
ManjuKumara GH
 
Modern Application Foundations: Underscore and Twitter Bootstrap
Howard Lewis Ship
 
Academy PRO: ES2015
Binary Studio
 
Extensible Operators and Literals for JavaScript
Brendan Eich
 
Workshop 10: ECMAScript 6
Visual Engineering
 
Web futures
Brendan Eich
 
Introduction to ECMAScript 2015
Tomasz Dziuda
 
Elm: give it a try
Eugene Zharkov
 
Reason - introduction to language and its ecosystem | Łukasz Strączyński
Grand Parade Poland
 
Coscup2021 - useful abstractions at rust and it's practical usage
Wayne Tsai
 
Practical JavaScript Programming - Session 7/8
Wilson Su
 
ECMAScript 6 major changes
hayato
 
Workshop 1: Good practices in JavaScript
Visual Engineering
 
Value Objects, Full Throttle (to be updated for spring TC39 meetings)
Brendan Eich
 
ECMAScript 6
WebF
 
Functional Patterns for the non-mathematician
Brian Lonsdorf
 
ECMAScript 6 new features
GephenSG
 
Configurable Pattern Matching Semantics in openCypher: Defining Levels of Nod...
openCypher
 
ECMA5 and ES6 Promises
Oswald Campesato
 
ECMAScript2015
qmmr
 
Data weave 2.0 advanced (recursion, pattern matching)
ManjuKumara GH
 
Ad

More from François-Guillaume Ribreau (16)

PDF
REX LEAN- Créer un SaaS et être rentable après 6 mois
François-Guillaume Ribreau
 
PDF
⛳️ Votre API passe-t-elle le contrôle technique ?
François-Guillaume Ribreau
 
PDF
Choisir entre une API RPC, SOAP, REST, GraphQL? 
Et si le problème était ai...
François-Guillaume Ribreau
 
PDF
He stopped using for/while loops, you won't believe what happened next!
François-Guillaume Ribreau
 
PDF
Une plateforme moderne pour le groupe SIPA/Ouest-France 
François-Guillaume Ribreau
 
PDF
[BreizhCamp, format 15min] Construire et automatiser l'ecosystème de son Saa...
François-Guillaume Ribreau
 
PDF
[BreizhCamp, format 15min] Une api rest et GraphQL sans code grâce à PostgR...
François-Guillaume Ribreau
 
PDF
RedisConf 2016 - Redis usage and ecosystem
François-Guillaume Ribreau
 
PDF
Automatic constraints as a team maturity accelerator for startups
François-Guillaume Ribreau
 
PDF
Development Principles & Philosophy
François-Guillaume Ribreau
 
PDF
Les enjeux de l'information et de l'algorithmique dans notre société
François-Guillaume Ribreau
 
PDF
How I monitor SaaS products
François-Guillaume Ribreau
 
PDF
Continous Integration of (JS) projects & check-build philosophy
François-Guillaume Ribreau
 
PDF
Introduction to Redis
François-Guillaume Ribreau
 
PDF
Approfondissement CSS3
François-Guillaume Ribreau
 
PDF
Découverte HTML5/CSS3
François-Guillaume Ribreau
 
REX LEAN- Créer un SaaS et être rentable après 6 mois
François-Guillaume Ribreau
 
⛳️ Votre API passe-t-elle le contrôle technique ?
François-Guillaume Ribreau
 
Choisir entre une API RPC, SOAP, REST, GraphQL? 
Et si le problème était ai...
François-Guillaume Ribreau
 
He stopped using for/while loops, you won't believe what happened next!
François-Guillaume Ribreau
 
Une plateforme moderne pour le groupe SIPA/Ouest-France 
François-Guillaume Ribreau
 
[BreizhCamp, format 15min] Construire et automatiser l'ecosystème de son Saa...
François-Guillaume Ribreau
 
[BreizhCamp, format 15min] Une api rest et GraphQL sans code grâce à PostgR...
François-Guillaume Ribreau
 
RedisConf 2016 - Redis usage and ecosystem
François-Guillaume Ribreau
 
Automatic constraints as a team maturity accelerator for startups
François-Guillaume Ribreau
 
Development Principles & Philosophy
François-Guillaume Ribreau
 
Les enjeux de l'information et de l'algorithmique dans notre société
François-Guillaume Ribreau
 
How I monitor SaaS products
François-Guillaume Ribreau
 
Continous Integration of (JS) projects & check-build philosophy
François-Guillaume Ribreau
 
Introduction to Redis
François-Guillaume Ribreau
 
Approfondissement CSS3
François-Guillaume Ribreau
 
Découverte HTML5/CSS3
François-Guillaume Ribreau
 
Ad

Recently uploaded (20)

PDF
Fl Studio 24.2.2 Build 4597 Crack for Windows Free Download 2025
faizk77g
 
PDF
Achieving Consistent and Reliable AI Code Generation - Medusa AI
medusaaico
 
PDF
The Rise of AI and IoT in Mobile App Tech.pdf
IMG Global Infotech
 
PDF
Bitcoin for Millennials podcast with Bram, Power Laws of Bitcoin
Stephen Perrenod
 
PPTX
OpenID AuthZEN - Analyst Briefing July 2025
David Brossard
 
PDF
Building Real-Time Digital Twins with IBM Maximo & ArcGIS Indoors
Safe Software
 
PDF
HubSpot Main Hub: A Unified Growth Platform
Jaswinder Singh
 
PDF
NewMind AI - Journal 100 Insights After The 100th Issue
NewMind AI
 
PDF
Newgen 2022-Forrester Newgen TEI_13 05 2022-The-Total-Economic-Impact-Newgen-...
darshakparmar
 
PPTX
AI Penetration Testing Essentials: A Cybersecurity Guide for 2025
defencerabbit Team
 
PPTX
AUTOMATION AND ROBOTICS IN PHARMA INDUSTRY.pptx
sameeraaabegumm
 
PDF
Transcript: New from BookNet Canada for 2025: BNC BiblioShare - Tech Forum 2025
BookNet Canada
 
PDF
Smart Trailers 2025 Update with History and Overview
Paul Menig
 
PDF
POV_ Why Enterprises Need to Find Value in ZERO.pdf
darshakparmar
 
PPTX
Building Search Using OpenSearch: Limitations and Workarounds
Sease
 
PDF
From Code to Challenge: Crafting Skill-Based Games That Engage and Reward
aiyshauae
 
PDF
DevBcn - Building 10x Organizations Using Modern Productivity Metrics
Justin Reock
 
PDF
Newgen Beyond Frankenstein_Build vs Buy_Digital_version.pdf
darshakparmar
 
PDF
LLMs.txt: Easily Control How AI Crawls Your Site
Keploy
 
PDF
Blockchain Transactions Explained For Everyone
CIFDAQ
 
Fl Studio 24.2.2 Build 4597 Crack for Windows Free Download 2025
faizk77g
 
Achieving Consistent and Reliable AI Code Generation - Medusa AI
medusaaico
 
The Rise of AI and IoT in Mobile App Tech.pdf
IMG Global Infotech
 
Bitcoin for Millennials podcast with Bram, Power Laws of Bitcoin
Stephen Perrenod
 
OpenID AuthZEN - Analyst Briefing July 2025
David Brossard
 
Building Real-Time Digital Twins with IBM Maximo & ArcGIS Indoors
Safe Software
 
HubSpot Main Hub: A Unified Growth Platform
Jaswinder Singh
 
NewMind AI - Journal 100 Insights After The 100th Issue
NewMind AI
 
Newgen 2022-Forrester Newgen TEI_13 05 2022-The-Total-Economic-Impact-Newgen-...
darshakparmar
 
AI Penetration Testing Essentials: A Cybersecurity Guide for 2025
defencerabbit Team
 
AUTOMATION AND ROBOTICS IN PHARMA INDUSTRY.pptx
sameeraaabegumm
 
Transcript: New from BookNet Canada for 2025: BNC BiblioShare - Tech Forum 2025
BookNet Canada
 
Smart Trailers 2025 Update with History and Overview
Paul Menig
 
POV_ Why Enterprises Need to Find Value in ZERO.pdf
darshakparmar
 
Building Search Using OpenSearch: Limitations and Workarounds
Sease
 
From Code to Challenge: Crafting Skill-Based Games That Engage and Reward
aiyshauae
 
DevBcn - Building 10x Organizations Using Modern Productivity Metrics
Justin Reock
 
Newgen Beyond Frankenstein_Build vs Buy_Digital_version.pdf
darshakparmar
 
LLMs.txt: Easily Control How AI Crawls Your Site
Keploy
 
Blockchain Transactions Explained For Everyone
CIFDAQ
 

Implementing pattern-matching in JavaScript (full version)

  • 2. @FGRibreau Implementing pattern-matching in JavaScript …or how to play with EcmaScript shortcoming
  • 4. @FGRibreau “In computer science, pattern matching is the act of checking a given sequence of tokens for the presence of the constituents of some pattern.” — wikipedia
  • 5. @FGRibreau def toYesOrNo(choice: Int): String = choice match { case 1 => "yes" case 0 => "no" case _ => "error" } def fact(n: Int): Int = n match { case 0 => 1 case n => n * fact(n - 1) } Pattern Matching in Scala
  • 6. @FGRibreau let rec factorial = function | 0 -> 1 | n -> n * factorial(n - 1);; Pattern Matching in OCaml
  • 8. @FGRibreau Une syntaxe de pattern-matching en JS ? Sortez l'artillerie lourde.
  • 9. @FGRibreau _.flatten(links).map(link => { [{protocol: 'HTTP'}]: => 1, [{protocol: 'AMQP'}]: => 2 }); It would it be awesome to use some pattern-matching there right?
  • 10. @FGRibreau SyntaxError: /Users/FG/www/iadvize-services- orchestration-tools/src/api/src/repositoryManagers/ github.js: Unexpected token (185:32) 183 | 184 | _.flatten(links).forEach(link => { > 185 | [{protocol: 'HTTP'}]: => 1, | ^ 186 | [{protocol: 'AMQP'}]: => 2 187 | }); 188 | ... and of course it’s not JS valid syntax
  • 14. @FGRibreau links.map(link => { [{protocol: 'HTTP'}]: => 1, [{protocol: 'AMQP'}]: => 2 }); Ok, so this is not valid, what precisely is not valid and how can we make it valid?
  • 15. @FGRibreau links.map(link => { [{protocol: 'HTTP'}]: () => 1, [{protocol: ‘AMQP'}]: () => 2 }); links.map(link => { [{protocol: 'HTTP'}]: 1, [{protocol: ‘AMQP'}]: 2 }); links.map(link => { [{protocol: 'HTTP'}]: => 1, [{protocol: 'AMQP'}]: => 2 });
  • 16. @FGRibreau { [{protocol: ‘HTTP'}]: 1, [{protocol: ‘AMQP’}]: 2 } The rest is syntactically valid ES6 "computed property names" { [{protocol: ‘HTTP’}]: () => 1, [{protocol: ‘AMQP’}]: () => 2 }
  • 17. @FGRibreau links.map(link => {}) [undefined, undefined] links.map(link => {1}) [undefined, undefined] links.map(link => {return 1}) [1,1] links.map(link => 1) [1,1] Syntactically valid, semantically invalid … but then I won't have my pattern matching.
  • 19. @FGRibreau If I go from there… _.flatten(links).map(link => { [{protocol: 'HTTP'}]: => 1, [{protocol: 'AMQP'}]: => 2 });
  • 20. @FGRibreau If I go from there… _.flatten(links).map(link => { [{protocol: 'HTTP'}]: => 1, [{protocol: 'AMQP'}]: => 2 }); _.flatten(links).map(match({ [{protocol: 'HTTP'}]: 1, [{protocol: 'AMQP'}]: 2 })); …to there…
  • 21. @FGRibreau … then it’s syntactically correct! _.flatten(links).map(match({ [{protocol: 'HTTP'}]: 1, [{protocol: 'AMQP'}]: 2 }));
  • 22. @FGRibreau … then it’s syntactically correct! _.flatten(links).map(match({ [{protocol: 'HTTP'}]: 1, [{protocol: 'AMQP'}]: 2 }));
  • 23. @FGRibreau … would be great too ! const linkNumber = match(link,{ [{protocol: 'HTTP'}]: 1, [{protocol: 'AMQP'}]: 2 });
  • 25. @FGRibreau { [{protocol: ‘HTTP'}]: 1, [{protocol: ‘AMQP’}]: 2 } ES6 "computed property names" “The object initializer syntax also supports computed property names. That allows you to put an expression in brackets [], that will be computed as the property name.”
  • 26. @FGRibreau { '[object Object]': 2 } evaluates to ES6 "computed property names" { [{protocol: 'HTTP'}]: 1, [{protocol: 'AMQP'}]: 2 }
  • 29. @FGRibreau evaluates to ES6 "computed property names" { [when({protocol: ‘HTTP’})]: 1, [when({protocol: ‘AMQP'})]: 2, [when()]: 0, } { '{"protocol":"HTTP"}': 1, '{"protocol":"AMQP"}': 2, Symbol('match.pattern.catchAll'): 0 }
  • 30. @FGRibreau evaluates to ES6 "computed property names" { [when({protocol: ‘HTTP’})]: 1, [when({protocol: ‘AMQP'})]: 2, [when()]: 0, } { '{"protocol":"HTTP"}': 1, '{"protocol":"AMQP"}': 2, Symbol('match.pattern.catchAll'): 0 }
  • 31. @FGRibreau ES6 "computed property names" { [when({protocol: ‘HTTP’})]: 1, [when({protocol: ‘AMQP'})]: 2, [when()]: 0, } { '{"protocol":"HTTP"}': 1, '{"protocol":"AMQP"}': 2, Symbol('match.pattern.catchAll'): 0 } function when(props){ if(props === undefined){ return _catchAllSymbol; } return _JSON.stringify(props); }
  • 32. @FGRibreau Order is lost by match’s object .map(match({ [when.range(0, 43)]: 42, [when(42)]: 72 })) when.range(0,43) => '["Symbol(match.pattern.RANGE)",0,43]' when(42) => '[42]' JS objects are an unordered collection of properties
  • 33. @FGRibreau Fixing properties declaration ? .map(match({ [when.range(0, 43)]: 42, [when(42)]: 72 })) .map(match(new Map([ [ when.range(0, 43), 42 ], [ when(42), 72 ], [ when(), 'never should be hit' ] ]))) NO!
  • 34. @FGRibreau Fixing properties declaration ? .map(match({ [when.range(0, 43)]: 42, [when(42)]: 72 })) .map(match([ [ when.range(0, 43), 42 ], [ when(42), 72 ], [ when(), 'never should be hit' ] ])) NO!
  • 37. @FGRibreau Fixing properties declaration .map(match({ [when.range(0, 43)]: 42, [when(42)]: 72 })) - same callsite - single thread - sequential evaluation
  • 38. @FGRibreau Fixing properties declaration .map(match({ [when.range(0, 43)]: 42, [when(42)]: 72 })) - same callsite - single thread - sequential evaluation
  • 39. @FGRibreau Fixing properties declaration when.range(0,43) => '[0, "Symbol(match.pattern.RANGE)",0,43]' when(42) => '[1, 42]' … #problemSolved … .map(match({ [when.range(0, 43)]: 42, [when(42)]: 72 })) - same callsite - single thread - sequential evaluation
  • 40. @FGRibreau How to get matched value? const fact = match({ [when(0)]: 1, [when()]: n * fact(n-1) }); fact(10); n is not defined
  • 41. @FGRibreau How to get matched value? const fact = match({ [when(0)]: 1, [when()]: n * fact(n-1) }); fact(10); simple, use a function: const fact = match({ [when(0)]: 1, [when()]: (n) => n * fact(n-1) }); fact(10); // 3628800
  • 42. @FGRibreau How to get matched value? const fact = match({ [when(0)]: 1, [when()]: n * fact(n-1) }); fact(10); simple, use a function: const fact = match({ [when(0)]: 1, [when()]: (n) => n * fact(n-1) }); fact(10); // 3628800
  • 43. @FGRibreau const input = [{protocol: 'HTTP', i:10}, {protocol: 'AMQP', i:11}, {protocol: 'WAT', i:3}]; const output = input.map(match({ [when({protocol:'HTTP'})]: (i) => () => 1, [when({protocol:'AMQP'})]: (i) => () => 2, [when()]: (i) => () => 0, })); output.map((f) => f()) // => [1, 2, 20] But how do I yield functions?
  • 45. @FGRibreau function match(/* args... */){ const args = Array.from(arguments), obj = args[args.length-1]; // pre-compute matchers let matchers = []; for(let key in obj){ matchers.push(when.unserialize(key, obj[key])); // e.g. {match:(mixed)=>boolean,result:mixed,position:number} } matchers.sort(function(a, b){ return a.position < b.position ? -1 : 1; }); if(Object.getOwnPropertySymbols(obj).indexOf(_catchAllSymbol) !== -1){ matchers.push(when.unserialize(_catchAllSymbol, obj[_catchAllSymbol])); } const calculateResult = function(input){ const matched = matchers.find((matcher) => matcher.match(input)); if (!matched) { throw new MissingCatchAllPattern(); } return typeof matched.result === 'function' ? matched.result(input) : matched.result; }; return args.length === 2 ? calculateResult(args[0]) : calculateResult; }
  • 46. @FGRibreau when.unserialize = function(serializedKey, value){ if(serializedKey === _catchAllSymbol){ return { match: _true, // same as: () => true result: value, position: Infinity }; } const {position, matcherConfiguration} = _unserialize(serializedKey); return { match: _match(matcherConfiguration), result: value, position: position }; };
  • 47. @FGRibreau function _match(props){ // [{type}, …] if(Array.isArray(props)){ if(props[0] === _patternORStr){ // _patternORStr = Symbol(‘OR’) props.shift(); return function(input){ return props[0].some((prop) => _matching(prop, input)); }; } if(props[0] === _patternANDStr){ // _patternANDStr = Symbol(‘AND’) props.shift(); return function(input){ return props[0].every((prop) => _matching(prop, input)); }; } if(props[0] === _patternRANGEStr){ // _patternRANGEStr = Symbol(‘RANGE’) props.shift(); return function(input){ return props[0] <= input && input <= props[1]; }; } } function _matching(props, input){ // [...] if(props instanceof RegExp){ return props.test(input); } if(typeof input === 'object'){ for(let prop in props){ if(input.hasOwnProperty(prop) && input[prop] !== props[prop]){ return false; } } return true; } return props === input; } return (input) => _matching(props, input); }
  • 52. @FGRibreau function replaceInstanceId(elem) { if (_.isPlainObject(elem)) { return _.mapValues(elem, replaceInstanceId); } else if (_.isArray(elem)) { return _.map(elem, replaceInstanceId); } else if (_.isString(elem) && _.includes(elem, 'instance_id')) { return _.template(elem)({instance_id: conf.instance_id}); } return elem; } Type Matching const replaceInstanceId = match({ [when(Object)]:(elem) => _.mapValues(elem, replaceInstanceId), [when(Array)]:(elem) => _.map(elem, replaceInstanceId), [when.and(String, /instance_id/)]: (elem) => _.template(elem)({instance_id: conf.instance_id}), [when()]: _.identity }); With type matching
  • 53. @FGRibreau const replaceInstanceId = match({ // extraction [when({a:when._, b:when._, c:{e:when._})]:(a, b, e) => {a, b, e}, [when(Array)]:(elem) => _.map(elem, replaceInstanceId), [when()]: _.identity }); Pattern Extraction
  • 55. @FGRibreau François-Guillaume RIBREAU @FGRibreau Tightly crafted developer oriented online real-time monitoring and administration service for Redis. Join us Frontend Dev - Backend Dev Fullstack Dev - DevOps #scala #nodejs #react #docker #xmpp