SlideShare une entreprise Scribd logo
1  sur  55
Télécharger pour lire hors ligne
@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

Contenu connexe

Tendances

AST - the only true tool for building JavaScript
AST - the only true tool for building JavaScriptAST - the only true tool for building JavaScript
AST - the only true tool for building JavaScript
Ingvar Stepanyan
 
Java 8 Streams & Collectors : the Leuven edition
Java 8 Streams & Collectors : the Leuven editionJava 8 Streams & Collectors : the Leuven edition
Java 8 Streams & Collectors : the Leuven edition
José Paumard
 
JDK8 : parallel programming made (too ?) easy
JDK8 : parallel programming made (too ?) easyJDK8 : parallel programming made (too ?) easy
JDK8 : parallel programming made (too ?) easy
José Paumard
 
Exploiting the newer perl to improve your plugins
Exploiting the newer perl to improve your pluginsExploiting the newer perl to improve your plugins
Exploiting the newer perl to improve your plugins
Marian Marinov
 

Tendances (20)

Free your lambdas
Free your lambdasFree your lambdas
Free your lambdas
 
AST Rewriting Using recast and esprima
AST Rewriting Using recast and esprimaAST Rewriting Using recast and esprima
AST Rewriting Using recast and esprima
 
AST - the only true tool for building JavaScript
AST - the only true tool for building JavaScriptAST - the only true tool for building JavaScript
AST - the only true tool for building JavaScript
 
Don't Be Afraid of Abstract Syntax Trees
Don't Be Afraid of Abstract Syntax TreesDon't Be Afraid of Abstract Syntax Trees
Don't Be Afraid of Abstract Syntax Trees
 
JavaScript on the GPU
JavaScript on the GPUJavaScript on the GPU
JavaScript on the GPU
 
C++ Programs
C++ ProgramsC++ Programs
C++ Programs
 
Lambda and Stream Master class - part 1
Lambda and Stream Master class - part 1Lambda and Stream Master class - part 1
Lambda and Stream Master class - part 1
 
Introduction into ES6 JavaScript.
Introduction into ES6 JavaScript.Introduction into ES6 JavaScript.
Introduction into ES6 JavaScript.
 
Argon walkthru 1-26
Argon walkthru 1-26Argon walkthru 1-26
Argon walkthru 1-26
 
Java Boilerplate Busters
Java Boilerplate BustersJava Boilerplate Busters
Java Boilerplate Busters
 
Java 8 Streams & Collectors : the Leuven edition
Java 8 Streams & Collectors : the Leuven editionJava 8 Streams & Collectors : the Leuven edition
Java 8 Streams & Collectors : the Leuven edition
 
ES6 PPT FOR 2016
ES6 PPT FOR 2016ES6 PPT FOR 2016
ES6 PPT FOR 2016
 
Virtual Machine Constructions for Dummies
Virtual Machine Constructions for DummiesVirtual Machine Constructions for Dummies
Virtual Machine Constructions for Dummies
 
JDK8 : parallel programming made (too ?) easy
JDK8 : parallel programming made (too ?) easyJDK8 : parallel programming made (too ?) easy
JDK8 : parallel programming made (too ?) easy
 
Being functional in PHP (DPC 2016)
Being functional in PHP (DPC 2016)Being functional in PHP (DPC 2016)
Being functional in PHP (DPC 2016)
 
Exploiting the newer perl to improve your plugins
Exploiting the newer perl to improve your pluginsExploiting the newer perl to improve your plugins
Exploiting the newer perl to improve your plugins
 
From Zero to Application Delivery with NixOS
From Zero to Application Delivery with NixOSFrom Zero to Application Delivery with NixOS
From Zero to Application Delivery with NixOS
 
What's New in ES6 for Web Devs
What's New in ES6 for Web DevsWhat's New in ES6 for Web Devs
What's New in ES6 for Web Devs
 
Java Full Throttle
Java Full ThrottleJava Full Throttle
Java Full Throttle
 
JavaScript Functions
JavaScript FunctionsJavaScript Functions
JavaScript Functions
 

Similaire à Implementing pattern-matching in JavaScript (full version)

The Logical Burrito - pattern matching, term rewriting and unification
The Logical Burrito - pattern matching, term rewriting and unificationThe Logical Burrito - pattern matching, term rewriting and unification
The Logical Burrito - pattern matching, term rewriting and unification
Norman Richards
 
Scala presentation by Aleksandar Prokopec
Scala presentation by Aleksandar ProkopecScala presentation by Aleksandar Prokopec
Scala presentation by Aleksandar Prokopec
Loïc Descotte
 
Refactoring to Macros with Clojure
Refactoring to Macros with ClojureRefactoring to Macros with Clojure
Refactoring to Macros with Clojure
Dmitry Buzdin
 

Similaire à Implementing pattern-matching in JavaScript (full version) (20)

The Logical Burrito - pattern matching, term rewriting and unification
The Logical Burrito - pattern matching, term rewriting and unificationThe Logical Burrito - pattern matching, term rewriting and unification
The Logical Burrito - pattern matching, term rewriting and unification
 
Real life-coffeescript
Real life-coffeescriptReal life-coffeescript
Real life-coffeescript
 
ITT 2015 - Saul Mora - Object Oriented Function Programming
ITT 2015 - Saul Mora - Object Oriented Function ProgrammingITT 2015 - Saul Mora - Object Oriented Function Programming
ITT 2015 - Saul Mora - Object Oriented Function Programming
 
Making JavaScript Libraries More Approachable
Making JavaScript Libraries More ApproachableMaking JavaScript Libraries More Approachable
Making JavaScript Libraries More Approachable
 
GeoGebra JavaScript CheatSheet
GeoGebra JavaScript CheatSheetGeoGebra JavaScript CheatSheet
GeoGebra JavaScript CheatSheet
 
Short intro to ECMAScript
Short intro to ECMAScriptShort intro to ECMAScript
Short intro to ECMAScript
 
Scala @ TomTom
Scala @ TomTomScala @ TomTom
Scala @ TomTom
 
ES6 in Real Life
ES6 in Real LifeES6 in Real Life
ES6 in Real Life
 
Javascript
JavascriptJavascript
Javascript
 
Round PEG, Round Hole - Parsing Functionally
Round PEG, Round Hole - Parsing FunctionallyRound PEG, Round Hole - Parsing Functionally
Round PEG, Round Hole - Parsing Functionally
 
Damn Fine CoffeeScript
Damn Fine CoffeeScriptDamn Fine CoffeeScript
Damn Fine CoffeeScript
 
Ast transformations
Ast transformationsAst transformations
Ast transformations
 
Scala presentation by Aleksandar Prokopec
Scala presentation by Aleksandar ProkopecScala presentation by Aleksandar Prokopec
Scala presentation by Aleksandar Prokopec
 
AngularJS Testing
AngularJS TestingAngularJS Testing
AngularJS Testing
 
PHP Static Code Review
PHP Static Code ReviewPHP Static Code Review
PHP Static Code Review
 
Kotlin @ Coupang Backend 2017
Kotlin @ Coupang Backend 2017Kotlin @ Coupang Backend 2017
Kotlin @ Coupang Backend 2017
 
JavaScript Unit Testing with Jasmine
JavaScript Unit Testing with JasmineJavaScript Unit Testing with Jasmine
JavaScript Unit Testing with Jasmine
 
EcmaScript unchained
EcmaScript unchainedEcmaScript unchained
EcmaScript unchained
 
Google Guava for cleaner code
Google Guava for cleaner codeGoogle Guava for cleaner code
Google Guava for cleaner code
 
Refactoring to Macros with Clojure
Refactoring to Macros with ClojureRefactoring to Macros with Clojure
Refactoring to Macros with Clojure
 

Plus de François-Guillaume Ribreau

Plus de François-Guillaume Ribreau (16)

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

Dernier

Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Victor Rentea
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Safe Software
 

Dernier (20)

TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data DiscoveryTrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
TrustArc Webinar - Unlock the Power of AI-Driven Data Discovery
 
Spring Boot vs Quarkus the ultimate battle - DevoxxUK
Spring Boot vs Quarkus the ultimate battle - DevoxxUKSpring Boot vs Quarkus the ultimate battle - DevoxxUK
Spring Boot vs Quarkus the ultimate battle - DevoxxUK
 
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
Apidays New York 2024 - Passkeys: Developing APIs to enable passwordless auth...
 
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdfRising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
Rising Above_ Dubai Floods and the Fortitude of Dubai International Airport.pdf
 
2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...2024: Domino Containers - The Next Step. News from the Domino Container commu...
2024: Domino Containers - The Next Step. News from the Domino Container commu...
 
DBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor PresentationDBX First Quarter 2024 Investor Presentation
DBX First Quarter 2024 Investor Presentation
 
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
Apidays New York 2024 - The Good, the Bad and the Governed by David O'Neill, ...
 
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
Modular Monolith - a Practical Alternative to Microservices @ Devoxx UK 2024
 
Strategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a FresherStrategies for Landing an Oracle DBA Job as a Fresher
Strategies for Landing an Oracle DBA Job as a Fresher
 
Cyberprint. Dark Pink Apt Group [EN].pdf
Cyberprint. Dark Pink Apt Group [EN].pdfCyberprint. Dark Pink Apt Group [EN].pdf
Cyberprint. Dark Pink Apt Group [EN].pdf
 
MS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectorsMS Copilot expands with MS Graph connectors
MS Copilot expands with MS Graph connectors
 
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWEREMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
EMPOWERMENT TECHNOLOGY GRADE 11 QUARTER 2 REVIEWER
 
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers:  A Deep Dive into Serverless Spatial Data and FMECloud Frontiers:  A Deep Dive into Serverless Spatial Data and FME
Cloud Frontiers: A Deep Dive into Serverless Spatial Data and FME
 
FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024FWD Group - Insurer Innovation Award 2024
FWD Group - Insurer Innovation Award 2024
 
Ransomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdfRansomware_Q4_2023. The report. [EN].pdf
Ransomware_Q4_2023. The report. [EN].pdf
 
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
Apidays New York 2024 - Accelerating FinTech Innovation by Vasa Krishnan, Fin...
 
AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024AXA XL - Insurer Innovation Award Americas 2024
AXA XL - Insurer Innovation Award Americas 2024
 
Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...Apidays New York 2024 - The value of a flexible API Management solution for O...
Apidays New York 2024 - The value of a flexible API Management solution for O...
 
MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024MINDCTI Revenue Release Quarter One 2024
MINDCTI Revenue Release Quarter One 2024
 
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
Apidays New York 2024 - APIs in 2030: The Risk of Technological Sleepwalk by ...
 

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