SlideShare a Scribd company logo
Stamps
Rethinking best practices
medium.com/@koresar
ctor()
method
X()
property A
ctor()
method
X()
property Aproperty B
ctor()
property A
property B
method
Y()
method
Z()
method
X()
ctor()
method
X()
property A
property Bmethod
Y()
method
Z()
…
ctor()
method X()
property A
property B
method Y()
method Z()
property C
ctor(override)
pony
Zuckerberg
yolo
the god
the true god
the new true
god
useless shit
useful thing()
leprosarium
matrix
new useless
shit
hell on Earth
pain
suffer
Typical class inheritance story
But actually I needed only
these:
ctor()
property B
method X()
useful thing()
Typical class inheritance story
• 1 constructor
• 211 methods
• 130 properties
• 55 events
(which essentially are properties
too)
• 1 static field
• 1 attribute
Rethinking inheritance
{
ctor()
method X()
property B
useful thing()
}
…
ctor()
method
X()
property B
useful
thing
pain
suffer
hell on
Earth
useless
shit
property A
method
Y()
method
Z()
Zuckerber
g
property C
Java/C# developers are like
To make that happen we invented stamps
Stamps
…
ctor()
method
X()
property B
useful
thing
pain
suffer
hell on
Earth
useless
shit
property A
method
Y()
method
Z()
Zuckerber
g
property C
• Stamps are composable behaviours
• Stamps are not a library or a module
• Stamps are a specification (like
Promises)
• Various stamp implementations are
compatible with each other
• Stamps are factory functions (like
classes)
import compose from 'stamp-implementation';
// not a real module
Stamps
The only thing in the specification
const Ctor = compose({
initializers: [function (...args) {
console.log('Hello Node Ninjas')
}]
});
Ctor(...args); // “Hello Node Ninjas”
Stamps
Constructor aka initializer
const MethodX = compose({
methods: {
X() {
// ...
}
}
});
MethodX().X();
Stamps
A method
const PropertyB = compose({
properties: {
B: 42
}
});
PropertyB().B; // 42
Stamps
A property
const UsefulThing = compose({
staticProperties: {
usefulThing() {
// ...
}
}
);
UsefulThing.usefulThing();
Stamps
A static property (method)
const Stamp1 = compose(
Ctor, MethodX, PropertyB, UsefulThing);
Stamps
Composing stamps
const myObject = Stamp1();
myObject.X();
myObject.B; // 42
Stamp1.usefulThing();
Stamps
Using the Stamp1
ctor()
method
X()
property B
useful
thing
const Stamp1 = compose(
Ctor, MethodX, PropertyB, UsefulThing);
import compose from 'stamp-implementation';
const Ctor = compose({
initializers: [ function () { /* … */ } ]
});
const MethodX = compose({
methods: { X() { /* … */ } }
});
const PropertyB = compose({
properties: { B: 42 }
});
const UsefulThing = compose({
staticProperties: { usefulThing() { /* … */ } }
});
const Stamp1 = compose(Ctor, MethodX, PropertyB, UsefulThing);
const myObject = Stamp1();
myObject.X();
myObject.B; // 42
Stamp1.usefulThing();
Stamps
ctor()
method
X()
property B
useful
thing
const Stamp1 = compose({
initializers: [() => {
// ...
}],
methods: {
X() {
// ...
}
},
properties: {
B: 42
},
staticProperties: {
usefulThing() {
// ...
}
}
});
const myObject = Stamp1();
myObject.X();
myObject.B; // 42
Stamp1.usefulThing();
Same but as a single stamp
stamp.compose() method
const Stamp1 = compose(Ctor, MethodX, PropertyB, UsefulThing);
Every compose call:
• creates a new stamp
• merges the metadata of the provided stamps
etc
const Stamp1 = Ctor.compose(MethodX, PropertyB, UsefulShit);
const Stamp1 = Ctor.compose(MethodX).compose(PropertyB).compose(UsefulThing);
const Stamp1 = Ctor.compose(MethodX.compose(PropertyB.compose(UsefulThing)));
const Stamp1 = compose(Ctor, MethodX).compose(PropertyB, UsefulThing);
Similar to Promises .then() Stamps have .compose()
Collected Stamp1 metadata
const Stamp1 = compose(Ctor, MethodX, PropertyB, UsefulThing);
console.log(Stamp1.compose);
{ [Function]
initializers: [ [Function] ],
methods: { X: [Function: X] },
properties: { B: 42 },
staticProperties: { usefulThing: [Function: usefulThing] } }
Stamp1.compose has:
property “initializers”
property “methods”
property “properties”
property “staticProperties”
ctor()
method
X()
property B
useful
thing
Now let’s take a classic Java example and
convert it to stamps.
The purpose of the example is not to solve a problem
but to show an idea behind the stamps.
@TesterInfo(
priority = Priority.HIGH,
createdBy = "Zavulon",
tags = {"sales","test"}
)
public class TestExample extends BaseTest {
TestExample(TestRunner r) {
this.runner = r;
}
@Test
void testA() {
// ...
}
@Test(enabled = false)
void testB() {
// …
}
}
Example: Java metadata and class configuration
Configuring class
metadata in Java
(annotations)
Configuring
object instance in
Java
(dependency injection)
Configuring class
members in Java
(interface implementation)
• class extension/inheritance
• Java annotations
• interface implementations
• dependency injection pattern
• has-a composition pattern
• is-a composition pattern
• proxy design pattern
• wrapper design pattern
• decorator design pattern
• …
How to setup a class behavior in Java?
This is nothing else, but
a process of configuring your class,
a process of collecting metadata
How to setup a stamp behavior?
• compose
• compose
• compose
• compose
• compose
• compose
• compose
• compose
• compose
• compose
stamp
const BaseTest = compose({
staticProperties: {
suite(info) {
return this.compose({
deepConfiguration: info
});
},
test(options, func) {
return this.compose({
methods: {
[func.name]: func
},
configuration: {
[func.name]: options
}
});
}
}
});
TesterStamp.suite()
Example: a stamp with two static methods
Example: compare Java and stamp
@TesterInfo(
priority = Priority.HIGH,
createdBy = "Zavulon",
tags = {"sales","test" }
)
public class TestExample
extends BaseTest {
TestExample(TestRunner r) {
this.runner = r;
}
@Test
void testA() {
// ...
}
@Test(enabled = false)
void testB() {
// …
}
}
const TestExample = BaseTest
.suite({
priority: Priority.HIGH,
createdBy: 'Zavulon',
tags: ['sales', 'test']
})
.compose({properties: {runner}})
.test(null,
function testA() {
// ...
}
)
.test({enabled: false},
function testB() {
// ...
}
);
const BaseTest = compose({
staticProperties: {
suite(info) {
return this.compose({
deepConfiguration: info
});
},
test(options, func) {
return this.compose({
methods: {
[func.name]: func
},
configuration: {
[func.name]: options
}
});
}
}
});
TesterStamp.suite().compose().test().test();
Example: a stamp with two static methods
Let’s see what the resulting stamp metadata looks like
{
deepConfiguration: {
priority: 'HIGH',
createdBy: 'Zavulon',
tags: ['sales', 'test']
},
properties: {
runner: ...
},
configuration: {
testA: {},
testB: {enabled: false},
testC: {enabled: true}
},
methods: {
testA() {},
testB() {},
testC() {}
}
}
TestExample.compose
Stamp’s metadata in specification
* methods - instance methods (prototype)
* properties - instance properties
* deepProperties - deeply merged instance prop
* propertyDescriptors - JavaScript standard pr
* staticProperties - stamp properties
* staticDeepProperties - deeply merged stamp p
* staticPropertyDescriptors - JavaScript stand
* initializers - list of initializers
* configuration - arbitrary data
* deepConfiguration - deeply merged arbitrary
The “magical” metadata merging algorithm
const dstMetadata = {};
mergeMetadata(dstMetadata, srcMetadata);
/**
* Combine two stamp metadata objects. Mutates `dst` object.
*/
function mergeMetadata(dst, src) {
_.assign(dst.methods, src.methods);
_.assign(dst.properties, src.properties);
_.assign(dst.propertyDescriptors, src.propertyDescriptors);
_.assign(dst.staticProperties, src.staticProperties);
_.assign(dst.staticPropertyDescriptors, src.staticPropertyDescriptors);
_.assign(dst.configuration, src.configuration);
_.merge(dst.deepProperties, src.deepProperties);
_.merge(dst.staticDeepProperties, src.staticDeepProperties);
_.merge(dst.deepConfiguration, src.deepConfiguration);
dst.initializers = dst.initializers.concat(src.initializers);
Awesome features you didn’t notice
The .compose() method is detachable
import {ThirdPartyStamp} from 'third-party-stuff';
I wish the .then() method of Promises was
as easy detachable as the .compose() method.
Like that:
const Promise = thirdPartyPromise.then;
Detaching the .compose() method
And reusing it as a compose() function to create new stamps
const compose = ThirdPartyStamp.compose;
const Stamp1 = compose({
properties: {
message: "Look Ma! I'm creating stamps without importing an imple
}
});
You can override the .compose()
import compose from 'stamp-specification';
function infectedCompose(...args) {
console.log('composing the following: ', args);
args.push({staticProperties: {compose: infectedCompose}});
return compose.apply(this, args);
}
const Ctor = infectedCompose({
initializers: [function () { /* ... */ }]
});
const MethodX = infectedCompose({
methods: { X() { /* ... */ } }
});
const PropertyB = infectedCompose({
properties: { B: 42 }
});
const UsefulThing = infectedCompose({
staticProperties: { usefulThing() { /* ... */ } }
});
const Stamp1 = infectedCompose(Ctor, MethodX)
.compose(PropertyB.compose(UsefulThing));
console.log gets
executed 7 times
{
You can create APIs like that
const MyUser = compose({
initializers: [function ({password}) {
this.password = password;
console.log(this.password.length);
}]
});
// Cannot read property 'password' of undefined
MyUser();
// Cannot read property 'length' of null
MyUser({password: null});
import MyUser from './my-user';
import ArgumentChecker from './argument-checker';
const MySafeUser = ArgumentChecker.checkArguments({
password: 'string'
})
.compose(MyUser);
// throws "Argument 'password' must be a string"
MySafeUser();
// throws "Argument 'password' must be a string"
MySafeUser({password: null});
You can create APIs like that
You can create APIs like that
1 import compose from 'stamp-specification';
2
3 const MyUser = compose({
4 initializers: [function ({password}) {
5 this.password = password;
6 console.log(this.password.length);
7 }]
8 });
9
10 // Cannot read property 'password' of undefined
11 MyUser();
12
13 // Cannot read property 'length' of null
14 MyUser({password: null});
15
16
17 const MySafeUser = ArgumentChecker.checkArguments({
18 password: 'string'
19 })
20 .compose(MyUser);
21
22 // Argument 'password' must be a string
23 MySafeUser();
const ArgumentChecker = compose({
staticProperties: {
checkArguments(keyValueMap) {
// deep merge all the pairs
// to the ArgumentChecker object
return this.compose({deepConfiguration: {
ArgumentChecker: keyValueMap
}});
}
},
...
ArgumentChecker stamp
...
initializers: [(options = {}, {stamp}) => {
// take the map of key-value pairs
// and iterate over it
const map = stamp.compose.deepConfiguration.ArgumentChec
for (const [argName, type] of map) {
if (typeof options[argName] !== type)
throw new Error(
`Argument "${argName}" must be a ${type}`);
}
}]
});
const ArgumentChecker = compose({
staticProperties: {
checkArguments(keyValueMap) {
// deep merge all the pairs to the ArgumentChecker object
return this.compose({deepConfiguration: {
ArgumentChecker: keyValueMap
}});
}
},
initializers: [function (options = {}, {stamp}) {
// take the map of key-value pairs and iterate over it
const map = stamp.compose.deepConfiguration.ArgumentChecker;
for (const [argName, type] of map) {
if (typeof options[argName] !== type)
throw new Error(
`Argument "${argName}" must be a ${type}`);
}
}]
});
ArgumentChecker stamp
ArgumentChecker stamp using stampit module
const ArgumentChecker = stampit() // <- creating a new empty stamp
.statics({
checkArguments(keyValueMap) {
return this.deepConf({ArgumentChecker: keyValueMap});
}
})
.init(function (options = {}, {stamp}) {
const map = stamp.compose.deepConfiguration.ArgumentChecker;
for (const [argName, type] of map) {
if (typeof options[argName] !== type)
throw new Error(`"${argName}" is missing`);
}
});
• Instead of classes obviously
• When you have many similar but different
models:
• games
(craft wooden old unique improved dwarf
sword)
• subscription types
(Free - Pro - Enterprise, yearly - monthly,
direct debit - invoice, credit card - bank
account, etc.)
• … your case
• As a dependency injection for complex
business logic
When to use Stamps
• When you need to squeeze every CPU cycle from yo
• games (LOL!)
• drivers
• In small utility modules (like left-pad)
When NOT to use Stamps
medium.com/@koresar
So, if you are building a new language, please,
omit classes.
Consider stamps instead
(or a similar composable behaviours)
Specs: https://github.com/stampit-org/stamp-specification
Development: https://github.com/stampit-org
stampit_jsNews:
kore_sar(C) Vasyl Boroviak
Chat: https://gitter.im/stampit-org/stampit

More Related Content

What's hot

openFrameworks 007 - utils
openFrameworks 007 - utilsopenFrameworks 007 - utils
openFrameworks 007 - utils
roxlu
 
Academy PRO: ES2015
Academy PRO: ES2015Academy PRO: ES2015
Academy PRO: ES2015
Binary Studio
 
Code as data as code.
Code as data as code.Code as data as code.
Code as data as code.Mike Fogus
 
PDBC
PDBCPDBC
PDBC
Sunil OS
 
Coscup2021-rust-toturial
Coscup2021-rust-toturialCoscup2021-rust-toturial
Coscup2021-rust-toturial
Wayne Tsai
 
ES6 in Real Life
ES6 in Real LifeES6 in Real Life
ES6 in Real Life
Domenic Denicola
 
ES6 - Next Generation Javascript
ES6 - Next Generation JavascriptES6 - Next Generation Javascript
ES6 - Next Generation Javascript
Ramesh Nair
 
Coscup2021 - useful abstractions at rust and it's practical usage
Coscup2021 - useful abstractions at rust and it's practical usageCoscup2021 - useful abstractions at rust and it's practical usage
Coscup2021 - useful abstractions at rust and it's practical usage
Wayne Tsai
 
EcmaScript 6
EcmaScript 6 EcmaScript 6
EcmaScript 6
Manoj Kumar
 
Grammarware Memes
Grammarware MemesGrammarware Memes
Grammarware Memes
Eelco Visser
 
The Ring programming language version 1.5.4 book - Part 31 of 185
The Ring programming language version 1.5.4 book - Part 31 of 185The Ring programming language version 1.5.4 book - Part 31 of 185
The Ring programming language version 1.5.4 book - Part 31 of 185
Mahmoud Samir Fayed
 
Naïveté vs. Experience
Naïveté vs. ExperienceNaïveté vs. Experience
Naïveté vs. Experience
Mike Fogus
 
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6Dmitry Soshnikov
 
TDC2016POA | Trilha .NET - C# como você nunca viu: conceitos avançados de pro...
TDC2016POA | Trilha .NET - C# como você nunca viu: conceitos avançados de pro...TDC2016POA | Trilha .NET - C# como você nunca viu: conceitos avançados de pro...
TDC2016POA | Trilha .NET - C# como você nunca viu: conceitos avançados de pro...
tdc-globalcode
 
The Ring programming language version 1.6 book - Part 35 of 189
The Ring programming language version 1.6 book - Part 35 of 189The Ring programming language version 1.6 book - Part 35 of 189
The Ring programming language version 1.6 book - Part 35 of 189
Mahmoud Samir Fayed
 
SDC - Einführung in Scala
SDC - Einführung in ScalaSDC - Einführung in Scala
SDC - Einführung in Scala
Christian Baranowski
 
Type script by Howard
Type script by HowardType script by Howard
Type script by Howard
LearningTech
 
JavaScript - i och utanför webbläsaren (2010-03-03)
JavaScript - i och utanför webbläsaren (2010-03-03)JavaScript - i och utanför webbläsaren (2010-03-03)
JavaScript - i och utanför webbläsaren (2010-03-03)
Anders Jönsson
 

What's hot (20)

openFrameworks 007 - utils
openFrameworks 007 - utilsopenFrameworks 007 - utils
openFrameworks 007 - utils
 
Academy PRO: ES2015
Academy PRO: ES2015Academy PRO: ES2015
Academy PRO: ES2015
 
Code as data as code.
Code as data as code.Code as data as code.
Code as data as code.
 
PDBC
PDBCPDBC
PDBC
 
Coscup2021-rust-toturial
Coscup2021-rust-toturialCoscup2021-rust-toturial
Coscup2021-rust-toturial
 
ES6 in Real Life
ES6 in Real LifeES6 in Real Life
ES6 in Real Life
 
Cpp tutorial
Cpp tutorialCpp tutorial
Cpp tutorial
 
ES6 - Next Generation Javascript
ES6 - Next Generation JavascriptES6 - Next Generation Javascript
ES6 - Next Generation Javascript
 
Coscup2021 - useful abstractions at rust and it's practical usage
Coscup2021 - useful abstractions at rust and it's practical usageCoscup2021 - useful abstractions at rust and it's practical usage
Coscup2021 - useful abstractions at rust and it's practical usage
 
EcmaScript 6
EcmaScript 6 EcmaScript 6
EcmaScript 6
 
Grammarware Memes
Grammarware MemesGrammarware Memes
Grammarware Memes
 
The Ring programming language version 1.5.4 book - Part 31 of 185
The Ring programming language version 1.5.4 book - Part 31 of 185The Ring programming language version 1.5.4 book - Part 31 of 185
The Ring programming language version 1.5.4 book - Part 31 of 185
 
Naïveté vs. Experience
Naïveté vs. ExperienceNaïveté vs. Experience
Naïveté vs. Experience
 
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
HelsinkiJS meet-up. Dmitry Soshnikov - ECMAScript 6
 
TDC2016POA | Trilha .NET - C# como você nunca viu: conceitos avançados de pro...
TDC2016POA | Trilha .NET - C# como você nunca viu: conceitos avançados de pro...TDC2016POA | Trilha .NET - C# como você nunca viu: conceitos avançados de pro...
TDC2016POA | Trilha .NET - C# como você nunca viu: conceitos avançados de pro...
 
ECMAScript 6
ECMAScript 6ECMAScript 6
ECMAScript 6
 
The Ring programming language version 1.6 book - Part 35 of 189
The Ring programming language version 1.6 book - Part 35 of 189The Ring programming language version 1.6 book - Part 35 of 189
The Ring programming language version 1.6 book - Part 35 of 189
 
SDC - Einführung in Scala
SDC - Einführung in ScalaSDC - Einführung in Scala
SDC - Einführung in Scala
 
Type script by Howard
Type script by HowardType script by Howard
Type script by Howard
 
JavaScript - i och utanför webbläsaren (2010-03-03)
JavaScript - i och utanför webbläsaren (2010-03-03)JavaScript - i och utanför webbläsaren (2010-03-03)
JavaScript - i och utanför webbläsaren (2010-03-03)
 

Similar to Stamps - a better way to object composition

Whats new in_csharp4
Whats new in_csharp4Whats new in_csharp4
Whats new in_csharp4Abed Bukhari
 
2012 JDays Bad Tests Good Tests
2012 JDays Bad Tests Good Tests2012 JDays Bad Tests Good Tests
2012 JDays Bad Tests Good Tests
Tomek Kaczanowski
 
C++20 the small things - Timur Doumler
C++20 the small things - Timur DoumlerC++20 the small things - Timur Doumler
C++20 the small things - Timur Doumler
corehard_by
 
33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good Tests33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good TestsTomek Kaczanowski
 
Imugi: Compiler made with Python
Imugi: Compiler made with PythonImugi: Compiler made with Python
Imugi: Compiler made with Python
Han Lee
 
Lezione03
Lezione03Lezione03
Lezione03
robynho86
 
Riga Dev Day 2016 - Having fun with Javassist
Riga Dev Day 2016 - Having fun with JavassistRiga Dev Day 2016 - Having fun with Javassist
Riga Dev Day 2016 - Having fun with Javassist
Anton Arhipov
 
java experiments and programs
java experiments and programsjava experiments and programs
java experiments and programs
Karuppaiyaa123
 
Go ahead, make my day
Go ahead, make my dayGo ahead, make my day
Go ahead, make my day
Tor Ivry
 
Functional programming using underscorejs
Functional programming using underscorejsFunctional programming using underscorejs
Functional programming using underscorejs偉格 高
 
Bind me if you can
Bind me if you canBind me if you can
Bind me if you can
Ovidiu Farauanu
 
Java agents are watching your ByteCode
Java agents are watching your ByteCodeJava agents are watching your ByteCode
Java agents are watching your ByteCode
Roman Tsypuk
 
The Ring programming language version 1.3 book - Part 83 of 88
The Ring programming language version 1.3 book - Part 83 of 88The Ring programming language version 1.3 book - Part 83 of 88
The Ring programming language version 1.3 book - Part 83 of 88
Mahmoud Samir Fayed
 
Object-Oriented Javascript
Object-Oriented JavascriptObject-Oriented Javascript
Object-Oriented Javascript
kvangork
 
Object-Oriented JavaScript
Object-Oriented JavaScriptObject-Oriented JavaScript
Object-Oriented JavaScript
kvangork
 
TypeScript Introduction
TypeScript IntroductionTypeScript Introduction
TypeScript Introduction
Dmitry Sheiko
 
From android/java to swift (3)
From android/java to swift (3)From android/java to swift (3)
From android/java to swift (3)
allanh0526
 
Lo Mejor Del Pdc2008 El Futrode C#
Lo Mejor Del Pdc2008 El Futrode C#Lo Mejor Del Pdc2008 El Futrode C#
Lo Mejor Del Pdc2008 El Futrode C#
Juan Pablo
 

Similar to Stamps - a better way to object composition (20)

Whats new in_csharp4
Whats new in_csharp4Whats new in_csharp4
Whats new in_csharp4
 
2012 JDays Bad Tests Good Tests
2012 JDays Bad Tests Good Tests2012 JDays Bad Tests Good Tests
2012 JDays Bad Tests Good Tests
 
C++20 the small things - Timur Doumler
C++20 the small things - Timur DoumlerC++20 the small things - Timur Doumler
C++20 the small things - Timur Doumler
 
33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good Tests33rd Degree 2013, Bad Tests, Good Tests
33rd Degree 2013, Bad Tests, Good Tests
 
Imugi: Compiler made with Python
Imugi: Compiler made with PythonImugi: Compiler made with Python
Imugi: Compiler made with Python
 
Lezione03
Lezione03Lezione03
Lezione03
 
Lezione03
Lezione03Lezione03
Lezione03
 
Riga Dev Day 2016 - Having fun with Javassist
Riga Dev Day 2016 - Having fun with JavassistRiga Dev Day 2016 - Having fun with Javassist
Riga Dev Day 2016 - Having fun with Javassist
 
Lecture5
Lecture5Lecture5
Lecture5
 
java experiments and programs
java experiments and programsjava experiments and programs
java experiments and programs
 
Go ahead, make my day
Go ahead, make my dayGo ahead, make my day
Go ahead, make my day
 
Functional programming using underscorejs
Functional programming using underscorejsFunctional programming using underscorejs
Functional programming using underscorejs
 
Bind me if you can
Bind me if you canBind me if you can
Bind me if you can
 
Java agents are watching your ByteCode
Java agents are watching your ByteCodeJava agents are watching your ByteCode
Java agents are watching your ByteCode
 
The Ring programming language version 1.3 book - Part 83 of 88
The Ring programming language version 1.3 book - Part 83 of 88The Ring programming language version 1.3 book - Part 83 of 88
The Ring programming language version 1.3 book - Part 83 of 88
 
Object-Oriented Javascript
Object-Oriented JavascriptObject-Oriented Javascript
Object-Oriented Javascript
 
Object-Oriented JavaScript
Object-Oriented JavaScriptObject-Oriented JavaScript
Object-Oriented JavaScript
 
TypeScript Introduction
TypeScript IntroductionTypeScript Introduction
TypeScript Introduction
 
From android/java to swift (3)
From android/java to swift (3)From android/java to swift (3)
From android/java to swift (3)
 
Lo Mejor Del Pdc2008 El Futrode C#
Lo Mejor Del Pdc2008 El Futrode C#Lo Mejor Del Pdc2008 El Futrode C#
Lo Mejor Del Pdc2008 El Futrode C#
 

Recently uploaded

2024 eCommerceDays Toulouse - Sylius 2.0.pdf
2024 eCommerceDays Toulouse - Sylius 2.0.pdf2024 eCommerceDays Toulouse - Sylius 2.0.pdf
2024 eCommerceDays Toulouse - Sylius 2.0.pdf
Łukasz Chruściel
 
OpenMetadata Community Meeting - 5th June 2024
OpenMetadata Community Meeting - 5th June 2024OpenMetadata Community Meeting - 5th June 2024
OpenMetadata Community Meeting - 5th June 2024
OpenMetadata
 
Why Mobile App Regression Testing is Critical for Sustained Success_ A Detail...
Why Mobile App Regression Testing is Critical for Sustained Success_ A Detail...Why Mobile App Regression Testing is Critical for Sustained Success_ A Detail...
Why Mobile App Regression Testing is Critical for Sustained Success_ A Detail...
kalichargn70th171
 
Launch Your Streaming Platforms in Minutes
Launch Your Streaming Platforms in MinutesLaunch Your Streaming Platforms in Minutes
Launch Your Streaming Platforms in Minutes
Roshan Dwivedi
 
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Crescat
 
Graspan: A Big Data System for Big Code Analysis
Graspan: A Big Data System for Big Code AnalysisGraspan: A Big Data System for Big Code Analysis
Graspan: A Big Data System for Big Code Analysis
Aftab Hussain
 
Utilocate provides Smarter, Better, Faster, Safer Locate Ticket Management
Utilocate provides Smarter, Better, Faster, Safer Locate Ticket ManagementUtilocate provides Smarter, Better, Faster, Safer Locate Ticket Management
Utilocate provides Smarter, Better, Faster, Safer Locate Ticket Management
Utilocate
 
May Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdfMay Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdf
Adele Miller
 
Enterprise Resource Planning System in Telangana
Enterprise Resource Planning System in TelanganaEnterprise Resource Planning System in Telangana
Enterprise Resource Planning System in Telangana
NYGGS Automation Suite
 
Empowering Growth with Best Software Development Company in Noida - Deuglo
Empowering Growth with Best Software  Development Company in Noida - DeugloEmpowering Growth with Best Software  Development Company in Noida - Deuglo
Empowering Growth with Best Software Development Company in Noida - Deuglo
Deuglo Infosystem Pvt Ltd
 
E-commerce Application Development Company.pdf
E-commerce Application Development Company.pdfE-commerce Application Development Company.pdf
E-commerce Application Development Company.pdf
Hornet Dynamics
 
SWEBOK and Education at FUSE Okinawa 2024
SWEBOK and Education at FUSE Okinawa 2024SWEBOK and Education at FUSE Okinawa 2024
SWEBOK and Education at FUSE Okinawa 2024
Hironori Washizaki
 
GOING AOT WITH GRAALVM FOR SPRING BOOT (SPRING IO)
GOING AOT WITH GRAALVM FOR  SPRING BOOT (SPRING IO)GOING AOT WITH GRAALVM FOR  SPRING BOOT (SPRING IO)
GOING AOT WITH GRAALVM FOR SPRING BOOT (SPRING IO)
Alina Yurenko
 
A Study of Variable-Role-based Feature Enrichment in Neural Models of Code
A Study of Variable-Role-based Feature Enrichment in Neural Models of CodeA Study of Variable-Role-based Feature Enrichment in Neural Models of Code
A Study of Variable-Role-based Feature Enrichment in Neural Models of Code
Aftab Hussain
 
AI Pilot Review: The World’s First Virtual Assistant Marketing Suite
AI Pilot Review: The World’s First Virtual Assistant Marketing SuiteAI Pilot Review: The World’s First Virtual Assistant Marketing Suite
AI Pilot Review: The World’s First Virtual Assistant Marketing Suite
Google
 
Essentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FMEEssentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FME
Safe Software
 
Artificia Intellicence and XPath Extension Functions
Artificia Intellicence and XPath Extension FunctionsArtificia Intellicence and XPath Extension Functions
Artificia Intellicence and XPath Extension Functions
Octavian Nadolu
 
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdf
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdfAutomated software refactoring with OpenRewrite and Generative AI.pptx.pdf
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdf
timtebeek1
 
Cracking the code review at SpringIO 2024
Cracking the code review at SpringIO 2024Cracking the code review at SpringIO 2024
Cracking the code review at SpringIO 2024
Paco van Beckhoven
 
Atelier - Innover avec l’IA Générative et les graphes de connaissances
Atelier - Innover avec l’IA Générative et les graphes de connaissancesAtelier - Innover avec l’IA Générative et les graphes de connaissances
Atelier - Innover avec l’IA Générative et les graphes de connaissances
Neo4j
 

Recently uploaded (20)

2024 eCommerceDays Toulouse - Sylius 2.0.pdf
2024 eCommerceDays Toulouse - Sylius 2.0.pdf2024 eCommerceDays Toulouse - Sylius 2.0.pdf
2024 eCommerceDays Toulouse - Sylius 2.0.pdf
 
OpenMetadata Community Meeting - 5th June 2024
OpenMetadata Community Meeting - 5th June 2024OpenMetadata Community Meeting - 5th June 2024
OpenMetadata Community Meeting - 5th June 2024
 
Why Mobile App Regression Testing is Critical for Sustained Success_ A Detail...
Why Mobile App Regression Testing is Critical for Sustained Success_ A Detail...Why Mobile App Regression Testing is Critical for Sustained Success_ A Detail...
Why Mobile App Regression Testing is Critical for Sustained Success_ A Detail...
 
Launch Your Streaming Platforms in Minutes
Launch Your Streaming Platforms in MinutesLaunch Your Streaming Platforms in Minutes
Launch Your Streaming Platforms in Minutes
 
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
Introducing Crescat - Event Management Software for Venues, Festivals and Eve...
 
Graspan: A Big Data System for Big Code Analysis
Graspan: A Big Data System for Big Code AnalysisGraspan: A Big Data System for Big Code Analysis
Graspan: A Big Data System for Big Code Analysis
 
Utilocate provides Smarter, Better, Faster, Safer Locate Ticket Management
Utilocate provides Smarter, Better, Faster, Safer Locate Ticket ManagementUtilocate provides Smarter, Better, Faster, Safer Locate Ticket Management
Utilocate provides Smarter, Better, Faster, Safer Locate Ticket Management
 
May Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdfMay Marketo Masterclass, London MUG May 22 2024.pdf
May Marketo Masterclass, London MUG May 22 2024.pdf
 
Enterprise Resource Planning System in Telangana
Enterprise Resource Planning System in TelanganaEnterprise Resource Planning System in Telangana
Enterprise Resource Planning System in Telangana
 
Empowering Growth with Best Software Development Company in Noida - Deuglo
Empowering Growth with Best Software  Development Company in Noida - DeugloEmpowering Growth with Best Software  Development Company in Noida - Deuglo
Empowering Growth with Best Software Development Company in Noida - Deuglo
 
E-commerce Application Development Company.pdf
E-commerce Application Development Company.pdfE-commerce Application Development Company.pdf
E-commerce Application Development Company.pdf
 
SWEBOK and Education at FUSE Okinawa 2024
SWEBOK and Education at FUSE Okinawa 2024SWEBOK and Education at FUSE Okinawa 2024
SWEBOK and Education at FUSE Okinawa 2024
 
GOING AOT WITH GRAALVM FOR SPRING BOOT (SPRING IO)
GOING AOT WITH GRAALVM FOR  SPRING BOOT (SPRING IO)GOING AOT WITH GRAALVM FOR  SPRING BOOT (SPRING IO)
GOING AOT WITH GRAALVM FOR SPRING BOOT (SPRING IO)
 
A Study of Variable-Role-based Feature Enrichment in Neural Models of Code
A Study of Variable-Role-based Feature Enrichment in Neural Models of CodeA Study of Variable-Role-based Feature Enrichment in Neural Models of Code
A Study of Variable-Role-based Feature Enrichment in Neural Models of Code
 
AI Pilot Review: The World’s First Virtual Assistant Marketing Suite
AI Pilot Review: The World’s First Virtual Assistant Marketing SuiteAI Pilot Review: The World’s First Virtual Assistant Marketing Suite
AI Pilot Review: The World’s First Virtual Assistant Marketing Suite
 
Essentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FMEEssentials of Automations: The Art of Triggers and Actions in FME
Essentials of Automations: The Art of Triggers and Actions in FME
 
Artificia Intellicence and XPath Extension Functions
Artificia Intellicence and XPath Extension FunctionsArtificia Intellicence and XPath Extension Functions
Artificia Intellicence and XPath Extension Functions
 
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdf
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdfAutomated software refactoring with OpenRewrite and Generative AI.pptx.pdf
Automated software refactoring with OpenRewrite and Generative AI.pptx.pdf
 
Cracking the code review at SpringIO 2024
Cracking the code review at SpringIO 2024Cracking the code review at SpringIO 2024
Cracking the code review at SpringIO 2024
 
Atelier - Innover avec l’IA Générative et les graphes de connaissances
Atelier - Innover avec l’IA Générative et les graphes de connaissancesAtelier - Innover avec l’IA Générative et les graphes de connaissances
Atelier - Innover avec l’IA Générative et les graphes de connaissances
 

Stamps - a better way to object composition

  • 3. ctor() method X() property A ctor() method X() property Aproperty B ctor() property A property B method Y() method Z() method X() ctor() method X() property A property Bmethod Y() method Z() … ctor() method X() property A property B method Y() method Z() property C ctor(override) pony Zuckerberg yolo the god the true god the new true god useless shit useful thing() leprosarium matrix new useless shit hell on Earth pain suffer Typical class inheritance story But actually I needed only these: ctor() property B method X() useful thing()
  • 4. Typical class inheritance story • 1 constructor • 211 methods • 130 properties • 55 events (which essentially are properties too) • 1 static field • 1 attribute
  • 5. Rethinking inheritance { ctor() method X() property B useful thing() } … ctor() method X() property B useful thing pain suffer hell on Earth useless shit property A method Y() method Z() Zuckerber g property C Java/C# developers are like
  • 6. To make that happen we invented stamps
  • 7. Stamps … ctor() method X() property B useful thing pain suffer hell on Earth useless shit property A method Y() method Z() Zuckerber g property C • Stamps are composable behaviours • Stamps are not a library or a module • Stamps are a specification (like Promises) • Various stamp implementations are compatible with each other • Stamps are factory functions (like classes)
  • 8. import compose from 'stamp-implementation'; // not a real module Stamps The only thing in the specification
  • 9. const Ctor = compose({ initializers: [function (...args) { console.log('Hello Node Ninjas') }] }); Ctor(...args); // “Hello Node Ninjas” Stamps Constructor aka initializer
  • 10. const MethodX = compose({ methods: { X() { // ... } } }); MethodX().X(); Stamps A method
  • 11. const PropertyB = compose({ properties: { B: 42 } }); PropertyB().B; // 42 Stamps A property
  • 12. const UsefulThing = compose({ staticProperties: { usefulThing() { // ... } } ); UsefulThing.usefulThing(); Stamps A static property (method)
  • 13. const Stamp1 = compose( Ctor, MethodX, PropertyB, UsefulThing); Stamps Composing stamps
  • 14. const myObject = Stamp1(); myObject.X(); myObject.B; // 42 Stamp1.usefulThing(); Stamps Using the Stamp1 ctor() method X() property B useful thing const Stamp1 = compose( Ctor, MethodX, PropertyB, UsefulThing);
  • 15. import compose from 'stamp-implementation'; const Ctor = compose({ initializers: [ function () { /* … */ } ] }); const MethodX = compose({ methods: { X() { /* … */ } } }); const PropertyB = compose({ properties: { B: 42 } }); const UsefulThing = compose({ staticProperties: { usefulThing() { /* … */ } } }); const Stamp1 = compose(Ctor, MethodX, PropertyB, UsefulThing); const myObject = Stamp1(); myObject.X(); myObject.B; // 42 Stamp1.usefulThing(); Stamps ctor() method X() property B useful thing
  • 16. const Stamp1 = compose({ initializers: [() => { // ... }], methods: { X() { // ... } }, properties: { B: 42 }, staticProperties: { usefulThing() { // ... } } }); const myObject = Stamp1(); myObject.X(); myObject.B; // 42 Stamp1.usefulThing(); Same but as a single stamp
  • 17. stamp.compose() method const Stamp1 = compose(Ctor, MethodX, PropertyB, UsefulThing); Every compose call: • creates a new stamp • merges the metadata of the provided stamps etc const Stamp1 = Ctor.compose(MethodX, PropertyB, UsefulShit); const Stamp1 = Ctor.compose(MethodX).compose(PropertyB).compose(UsefulThing); const Stamp1 = Ctor.compose(MethodX.compose(PropertyB.compose(UsefulThing))); const Stamp1 = compose(Ctor, MethodX).compose(PropertyB, UsefulThing); Similar to Promises .then() Stamps have .compose()
  • 18. Collected Stamp1 metadata const Stamp1 = compose(Ctor, MethodX, PropertyB, UsefulThing); console.log(Stamp1.compose); { [Function] initializers: [ [Function] ], methods: { X: [Function: X] }, properties: { B: 42 }, staticProperties: { usefulThing: [Function: usefulThing] } } Stamp1.compose has: property “initializers” property “methods” property “properties” property “staticProperties” ctor() method X() property B useful thing
  • 19. Now let’s take a classic Java example and convert it to stamps. The purpose of the example is not to solve a problem but to show an idea behind the stamps.
  • 20. @TesterInfo( priority = Priority.HIGH, createdBy = "Zavulon", tags = {"sales","test"} ) public class TestExample extends BaseTest { TestExample(TestRunner r) { this.runner = r; } @Test void testA() { // ... } @Test(enabled = false) void testB() { // … } } Example: Java metadata and class configuration Configuring class metadata in Java (annotations) Configuring object instance in Java (dependency injection) Configuring class members in Java (interface implementation)
  • 21. • class extension/inheritance • Java annotations • interface implementations • dependency injection pattern • has-a composition pattern • is-a composition pattern • proxy design pattern • wrapper design pattern • decorator design pattern • … How to setup a class behavior in Java? This is nothing else, but a process of configuring your class, a process of collecting metadata How to setup a stamp behavior? • compose • compose • compose • compose • compose • compose • compose • compose • compose • compose stamp
  • 22.
  • 23. const BaseTest = compose({ staticProperties: { suite(info) { return this.compose({ deepConfiguration: info }); }, test(options, func) { return this.compose({ methods: { [func.name]: func }, configuration: { [func.name]: options } }); } } }); TesterStamp.suite() Example: a stamp with two static methods
  • 24. Example: compare Java and stamp @TesterInfo( priority = Priority.HIGH, createdBy = "Zavulon", tags = {"sales","test" } ) public class TestExample extends BaseTest { TestExample(TestRunner r) { this.runner = r; } @Test void testA() { // ... } @Test(enabled = false) void testB() { // … } } const TestExample = BaseTest .suite({ priority: Priority.HIGH, createdBy: 'Zavulon', tags: ['sales', 'test'] }) .compose({properties: {runner}}) .test(null, function testA() { // ... } ) .test({enabled: false}, function testB() { // ... } );
  • 25. const BaseTest = compose({ staticProperties: { suite(info) { return this.compose({ deepConfiguration: info }); }, test(options, func) { return this.compose({ methods: { [func.name]: func }, configuration: { [func.name]: options } }); } } }); TesterStamp.suite().compose().test().test(); Example: a stamp with two static methods
  • 26. Let’s see what the resulting stamp metadata looks like { deepConfiguration: { priority: 'HIGH', createdBy: 'Zavulon', tags: ['sales', 'test'] }, properties: { runner: ... }, configuration: { testA: {}, testB: {enabled: false}, testC: {enabled: true} }, methods: { testA() {}, testB() {}, testC() {} } } TestExample.compose
  • 27. Stamp’s metadata in specification * methods - instance methods (prototype) * properties - instance properties * deepProperties - deeply merged instance prop * propertyDescriptors - JavaScript standard pr * staticProperties - stamp properties * staticDeepProperties - deeply merged stamp p * staticPropertyDescriptors - JavaScript stand * initializers - list of initializers * configuration - arbitrary data * deepConfiguration - deeply merged arbitrary
  • 28. The “magical” metadata merging algorithm const dstMetadata = {}; mergeMetadata(dstMetadata, srcMetadata); /** * Combine two stamp metadata objects. Mutates `dst` object. */ function mergeMetadata(dst, src) { _.assign(dst.methods, src.methods); _.assign(dst.properties, src.properties); _.assign(dst.propertyDescriptors, src.propertyDescriptors); _.assign(dst.staticProperties, src.staticProperties); _.assign(dst.staticPropertyDescriptors, src.staticPropertyDescriptors); _.assign(dst.configuration, src.configuration); _.merge(dst.deepProperties, src.deepProperties); _.merge(dst.staticDeepProperties, src.staticDeepProperties); _.merge(dst.deepConfiguration, src.deepConfiguration); dst.initializers = dst.initializers.concat(src.initializers);
  • 29. Awesome features you didn’t notice
  • 30. The .compose() method is detachable import {ThirdPartyStamp} from 'third-party-stuff'; I wish the .then() method of Promises was as easy detachable as the .compose() method. Like that: const Promise = thirdPartyPromise.then; Detaching the .compose() method And reusing it as a compose() function to create new stamps const compose = ThirdPartyStamp.compose; const Stamp1 = compose({ properties: { message: "Look Ma! I'm creating stamps without importing an imple } });
  • 31. You can override the .compose() import compose from 'stamp-specification'; function infectedCompose(...args) { console.log('composing the following: ', args); args.push({staticProperties: {compose: infectedCompose}}); return compose.apply(this, args); } const Ctor = infectedCompose({ initializers: [function () { /* ... */ }] }); const MethodX = infectedCompose({ methods: { X() { /* ... */ } } }); const PropertyB = infectedCompose({ properties: { B: 42 } }); const UsefulThing = infectedCompose({ staticProperties: { usefulThing() { /* ... */ } } }); const Stamp1 = infectedCompose(Ctor, MethodX) .compose(PropertyB.compose(UsefulThing)); console.log gets executed 7 times {
  • 32. You can create APIs like that const MyUser = compose({ initializers: [function ({password}) { this.password = password; console.log(this.password.length); }] }); // Cannot read property 'password' of undefined MyUser(); // Cannot read property 'length' of null MyUser({password: null});
  • 33. import MyUser from './my-user'; import ArgumentChecker from './argument-checker'; const MySafeUser = ArgumentChecker.checkArguments({ password: 'string' }) .compose(MyUser); // throws "Argument 'password' must be a string" MySafeUser(); // throws "Argument 'password' must be a string" MySafeUser({password: null}); You can create APIs like that
  • 34. You can create APIs like that 1 import compose from 'stamp-specification'; 2 3 const MyUser = compose({ 4 initializers: [function ({password}) { 5 this.password = password; 6 console.log(this.password.length); 7 }] 8 }); 9 10 // Cannot read property 'password' of undefined 11 MyUser(); 12 13 // Cannot read property 'length' of null 14 MyUser({password: null}); 15 16 17 const MySafeUser = ArgumentChecker.checkArguments({ 18 password: 'string' 19 }) 20 .compose(MyUser); 21 22 // Argument 'password' must be a string 23 MySafeUser();
  • 35. const ArgumentChecker = compose({ staticProperties: { checkArguments(keyValueMap) { // deep merge all the pairs // to the ArgumentChecker object return this.compose({deepConfiguration: { ArgumentChecker: keyValueMap }}); } }, ... ArgumentChecker stamp
  • 36. ... initializers: [(options = {}, {stamp}) => { // take the map of key-value pairs // and iterate over it const map = stamp.compose.deepConfiguration.ArgumentChec for (const [argName, type] of map) { if (typeof options[argName] !== type) throw new Error( `Argument "${argName}" must be a ${type}`); } }] });
  • 37. const ArgumentChecker = compose({ staticProperties: { checkArguments(keyValueMap) { // deep merge all the pairs to the ArgumentChecker object return this.compose({deepConfiguration: { ArgumentChecker: keyValueMap }}); } }, initializers: [function (options = {}, {stamp}) { // take the map of key-value pairs and iterate over it const map = stamp.compose.deepConfiguration.ArgumentChecker; for (const [argName, type] of map) { if (typeof options[argName] !== type) throw new Error( `Argument "${argName}" must be a ${type}`); } }] }); ArgumentChecker stamp
  • 38. ArgumentChecker stamp using stampit module const ArgumentChecker = stampit() // <- creating a new empty stamp .statics({ checkArguments(keyValueMap) { return this.deepConf({ArgumentChecker: keyValueMap}); } }) .init(function (options = {}, {stamp}) { const map = stamp.compose.deepConfiguration.ArgumentChecker; for (const [argName, type] of map) { if (typeof options[argName] !== type) throw new Error(`"${argName}" is missing`); } });
  • 39. • Instead of classes obviously • When you have many similar but different models: • games (craft wooden old unique improved dwarf sword) • subscription types (Free - Pro - Enterprise, yearly - monthly, direct debit - invoice, credit card - bank account, etc.) • … your case • As a dependency injection for complex business logic When to use Stamps
  • 40. • When you need to squeeze every CPU cycle from yo • games (LOL!) • drivers • In small utility modules (like left-pad) When NOT to use Stamps
  • 42. So, if you are building a new language, please, omit classes. Consider stamps instead (or a similar composable behaviours)

Editor's Notes

  1. This talk is highly complex. It implies that the listener is an experienced developer, or at least have extensive experience with classic OOP and classes. Apologies if this is too much. Ask me.
  2. ctor -> constructor
  3. The simplest Window UI element in .NET Framework is System.Windows.Controls.Label. According to MSDN its class in .NET v4.0 has:
  4. We are keeping the specification as small as possible
  5. Clarify statics
  6. I need to say that `compose` can accept both POJO and other stamps in no particular order.
  7. Stamps are immutable
  8. This is a typical Java unit test implementation of some unit tests.
  9. …When this though came to my mind the first time I was like:
  10. Suite is using this.compose() internally.
  11. explain the two functions on the bottom will not log if I won’t override the statics
  12. Kind of too much for such a simple feature. There is a shorter syntax using the stampit module.