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.toStringTag

https://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