Functional Programming and ES6
And some little nuances
Roman Numerals Kata ES5
var TOKENS = [
{token: 'X', value: 10},
{token: 'IX', value: 9},
{token: 'V', value: 5},
{token: 'IV', value: 4},
{token: 'I', value: 1}
];
module.exports = function arabic2roman(input) {
if (input <= 0) return '';
var highestToken = getHighestToken(input);
return highestToken.token + arabic2roman(input - highestToken.value);
};
function getHighestToken(input) {
return TOKENS.reduce(function(highestToken, token) {
if (highestToken) return highestToken;
if (input >= token.value) return token;
return null;
}, null);
}Whats wrong with this code?
Whats wrong?
var TOKENS = [
{token: 'X', value: 10},
{token: 'IX', value: 9},
{token: 'V', value: 5},
{token: 'IV', value: 4},
{token: 'I', value: 1}
];
module.exports = function arabic2roman(input) {
if (input <= 0) return '';
var highestToken = getHighestToken(input);
return highestToken.token + arabic2roman(input - highestToken.value);
};
function getHighestToken(input) {
return TOKENS.reduce(function(highestToken, token) {
if (highestToken) return highestToken;
if (input >= token.value) return token;
return null;
}, null);
}Tokens
var TOKENS = [
{token: 'X', value: 10},
{token: 'IX', value: 9},
{token: 'V', value: 5},
{token: 'IV', value: 4},
{token: 'I', value: 1}
];ES6 way
What is new?
const TOKENS = _Map.from({
I: 1,
V: 5,
X: 10
});
function* tokenGenerator() {
const reverseTokens = TOKENS.flip().reverse();
const ones = getOnes(reverseTokens);
const fives = getFives(reverseTokens);
const fours = getFours(ones, fives);
const nines = getNines(ones);
function* mix(...args) {
yield* args.map(a => a.last());
if (!args.every(a => a.size)) {
yield* mix(...args.map(a => a.take(a.size)));
}
}
yield* mix(nines, fives, fours, ones);
}
const DESCENDING_TOKENS = new _Map(tokenGenerator()); _Map.from
Array.from
function f() {
return Array.from(arguments);
}
f(1, 2, 3); // [1, 2, 3]
var s = new Set(["foo", window]);
Array.from(s); // ["foo", window]
Array.from("foo"); // ["f", "o", "o"]
Array.from([1, 2, 3], x => x + x); // [2, 4, 6]
Array.from({length: 5}, (v, k) => k); // [0, 1, 2, 3, 4]Extend Built in Map
class _Map extends Map {
constructor(collection) {
super(collection);
}
static from(object) {
return Object.keys(object).reduce((output, key) => {
output.set(key, object[key]);
return output;
}), new _Map);
}
}
What else is new?
const TOKENS = _Map.from({
I: 1,
V: 5,
X: 10
});
function* tokenGenerator() {
const reverseTokens = TOKENS.flip().reverse();
const ones = getOnes(reverseTokens);
const fives = getFives(reverseTokens);
const fours = getFours(ones, fives);
const nines = getNines(ones);
function* mix(...args) {
yield* args.map(a => a.last());
if (!args.every(a => a.size)) {
yield* mix(...args.map(a => a.take(a.size)));
}
}
yield* mix(nines, fives, fours, ones);
}
const DESCENDING_TOKENS = new _Map(tokenGenerator()); .flip and .reverse
class _Map extends Map {
/.../
reverse() {
return new _Map([...this].reverse());
}
flip() {
return new _Map([...this].map(([x, y]) => [y, x]));
}
/.../
}
const m = new Map();
m.set(1, 'a');
[...m] // [[1, 'a']]
const unique = [...new Set(arr)];Extending built-ins could lead to way cleaner code than how it was with lodash
lodash vs extend
// Lodash way
var users = [
{'user': 'barney', 'age': 36}, {'user': 'fred', 'age': 40}
];
var youngest = _.chain(users)
.sortBy('age')
.map(function(chr) {
return chr.user + ' is ' + chr.age;
})
.first()
.value();
// Extend way
var users = _Array.from([
{'user': 'barney', 'age': 36}, {'user': 'fred', 'age': 40}
]);
var youngest = users
.sortBy('age')
.map(function(chr) {
return chr.user + ' is ' + chr.age;
})
.first();ES7 bind operator
import { map, takeWhile, forEach } from "iterlib";
getPlayers()
::map(x => x.character())
::takeWhile(x => x.strength > 100)
::forEach(x => console.log(x));
let { find, html } = jake;
document.querySelectorAll("div.myClass")::find("p")::html("hahaha");What else is new?
const TOKENS = _Map.from({
I: 1,
V: 5,
X: 10
});
function* tokenGenerator() {
const reverseTokens = TOKENS.flip().reverse();
const ones = getOnes(reverseTokens);
const fives = getFives(reverseTokens);
const fours = getFours(ones, fives);
const nines = getNines(ones);
function* mix(...args) {
yield* args.map(a => a.last());
if (!args.every(a => a.size)) {
yield* mix(...args.map(a => a.take(a.size)));
}
}
yield* mix(nines, fives, fours, ones);
}
const DESCENDING_TOKENS = new _Map(tokenGenerator()); Const everywhere
iojs Document code style guidelines:
const should always be used over var for variables that never change
Basically
- Almost never use var
- Almost always use const
- Sometimes use let
What else is new?
const TOKENS = _Map.from({
I: 1,
V: 5,
X: 10
});
function* tokenGenerator() {
const reverseTokens = TOKENS.flip().reverse();
const ones = getOnes(reverseTokens);
const fives = getFives(reverseTokens);
const fours = getFours(ones, fives);
const nines = getNines(ones);
function* mix(...args) {
yield* args.map(a => a.last());
if (!args.every(a => a.size)) {
yield* mix(...args.map(a => a.take(a.size)));
}
}
yield* mix(nines, fives, fours, ones);
}
const DESCENDING_TOKENS = new _Map(tokenGenerator()); yield and yield*
Okay-okay!
But first:
what are the
iterables?
Iterables
const someString = 'hi';
const iterator = someString[Symbol.iterator]();
iterator.next(); // { value: "h", done: false }
iterator.next(); // { value: "i", done: false }
iterator.next(); // { value: undefined, done: true }
const idMaker = {
[Symbol.iterator]() {
let index = 0;
return {
next() {
return {value: index++, done: false};
}
};
}
}
for (let id of idMaker) {
console.log(id);
}
Iterables
function* gen() {
yield 1;
}
var iter = gen();
iter.next(); // { value: 1, done: false; }
iter.next(); // { value: undefined, done: true }
var someString = new String("hi");
// need to construct a String object explicitly to avoid auto-boxing
someString[Symbol.iterator] = gen;
[...someString] // [1]
someString + '!' // 'hi!'yield*
function* g1() {
yield 2;
yield 3;
}
function* g2() {
yield 1;
yield* g1();
yield 4;
}
var iterator = g2();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: 4, done: false }
console.log(iterator.next()); // { value: undefined, done: true }also yield*
function* g2() {
yield 1;
yield* [2, 3];
yield 4;
}
var iterator = g2();
console.log(iterator.next()); // { value: 1, done: false }
console.log(iterator.next()); // { value: 2, done: false }
console.log(iterator.next()); // { value: 3, done: false }
console.log(iterator.next()); // { value: 4, done: false }
console.log(iterator.next()); // { value: undefined, done: true }Tokens ES6
const TOKENS = _Map.from({
I: 1,
V: 5,
X: 10
});
function* tokenGenerator() {
const reverseTokens = TOKENS.flip().reverse();
const ones = getOnes(reverseTokens);
const fives = getFives(reverseTokens);
const fours = getFours(ones, fives);
const nines = getNines(ones);
function* mix(...args) {
yield* args.map(a => a.last());
if (!args.every(a => a.size)) {
yield* mix(...args.map(a => a.take(a.size)));
}
}
yield* mix(nines, fives, fours, ones);
}
const DESCENDING_TOKENS = new _Map(tokenGenerator());What else is wrong?
var TOKENS = [
{token: 'X', value: 10},
{token: 'IX', value: 9},
{token: 'V', value: 5},
{token: 'IV', value: 4},
{token: 'I', value: 1}
];
module.exports = function arabic2roman(input) {
if (input <= 0) return '';
var highestToken = getHighestToken(input);
return highestToken.token + arabic2roman(input - highestToken.value);
};
function getHighestToken(input) {
return TOKENS.reduce(function(highestToken, token) {
if (highestToken) return highestToken;
if (input >= token.value) return token;
return null;
}, null);
}More ES6-ish
module.exports = function arabic2roman(input) {
if (input <= 0) return '';
var highestToken = getHighestToken(input);
return highestToken.token + arabic2roman(input - highestToken.value);
};
function getHighestToken(input) {
return TOKENS.reduce(function(highestToken, token) {
if (highestToken) return highestToken;
if (input >= token.value) return token;
return null;
}, null);
}export default function arabic2roman(input) {
const [highestNumber, highestToken = ''] = getHighestToken(input);
return highestToken ? highestToken + arabic2roman(input - highestNumber) : '';
};
function getHighestToken(input) {
return DESCENDING_TOKENS.findEntry((_, key) => input >= key) || [];
}
don't have to use partial
var greet = function(greeting, name) {
return greeting + ' ' + name;
};
// lodash
['fred', 'barney'].forEach(_.partial(greet, 'hello'));
// → 'hello fred', 'hello barney',
['hi', 'hello'].forEach(_.partial(greet, _, 'fred'));
// → 'hi fred', 'hello fred'
// fat arrow
['fred', 'barney'].forEach(name => greet('hello', name));
// → 'hello fred', 'hello barney',
['hi', 'hello'].forEach(greeting => greet(greeting, fred));
// → 'hi fred'm 'hello fred'
// new use of _ :)
['hi', 'hello'].forEach((_, i) => console.log(i));Other things to look after
Symbol.iterator
Symbol.match
Symbol.replace
Symbol.search
Symbol.split
Symbol.hasInstance
Symbol.isConcatSpreadable
Symbol.unscopables
Symbol.species
Symbol.toPrimitive
Symbol.toStringTaghttps://developer.mozilla.org/en/docs/Web/JavaScript/Reference/Global_Objects/Symbol
Other things to look after
Reflect.get(target, name, [receiver])
Reflect.set(target, name, value, [receiver])
Reflect.has(target, name)
Reflect.apply(target, receiver, args)
Reflect.construct(target, args)
Reflect.getOwnPropertyDescriptor(target, name)
Reflect.defineProperty(target, name, desc)
Reflect.getPrototypeOf(target)
Reflect.setPrototypeOf(target, newProto)
Reflect.deleteProperty(target, name)
Reflect.enumerate(target)
Reflect.preventExtensions(target)
Reflect.isExtensible(target)
Reflect.ownKeys(target)http://h3manth.com/new/blog/2015/es6-reflect-api/
Other things to look after
http://www.2ality.com/2015/02/es6-classes-final.html
Functional Programming and ES6
By eggdice
Functional Programming and ES6
- 921