Currying with Ramda

Tryshchenko Oleksandr @ DataArt
tryshchenko.com

Functional Programming

  • It isn't new
  • You shouldn't use it just because it's trendy

1 + 2

R.add(1)(2)

Maybe.Just(1).ap(Maybe.Just(2).map(R.add)).just();

Please, don't make things overcomplicated

🙏

  • Encapsulate
  • Isolate
  • Compose

Pure / Impure


const x = 1;
const y = 2;

const impureFunction = () => x + y;
const pureFunction = (x, y) => x + y;

Pure / Impure


const a = {b: 1};

const impureFunction = () => a.b++;
const impureFunctionEither = (a) => a.b++;
const pureFunction = (a) => 
	Object.assign({}, a, {b: a.b + 1});

Pure / Impure


const a = {b: 1};

const impureFunction = () => a.b++;
const impureFunctionEither = (a) => {
  a.b++;
  return a;
}
const pureFunction = (a) => 
	Object.assign({}, a, {b: a.b + 1});

Real life example - Redux Reducers

Mutations


const a = {b: 1};

// mutation
a.b = 2;
[1, 3, 2].sort();

// immutable
const c = Object.assign({}, a, {b: 3});
const newArr = [1, 2, 3].map(x => x * 2);

Real life example - Redux state

Currying

Currying


const add = (a) => (b) => a + b;
add(1)(5) === 6;


const dbSaver = (dbConnection) => 
	(obj) => dbConnection.save(obj);

const save = dbBuilder(db);
save({city: "Wrocław"});
save({city: "Poznań"});

Ramda?

246

Curried helpers

What is Ramda?

  • 246 Curried Helpers
  • It partially overlaps with lodash
  • But behavior is often different

Curry


const addFn = (a, b, c) => a + b + c;

const curriedAddFn = R.curry(addFn);
curriedAddFn(1, 2)(3); // 6


['3', '3', '3', '3'].map(parseInt);


// [3, NaN, NaN, NaN]


const prsInt = R.curry(parseInt)(R.__, 10);
['3', '3', '3', '3'].map(prsInt);

// [3, 3, 3, 3]


const prsInt = (val) => parseInt(value, 10);
['3', '3', '3', '3'].map(prsInt);

// [3, 3, 3, 3]

R.__

Conditional

All and Equals


if (var1 === true 	
  && var2 === true 
  && var3 === true 
  && var4 === true) {
	...
}


const var1 = 1;
const var2 = [1]
if (var1 && var2) {
  console.log('coercion loves you ;)');
};

All and Equals


const isTruthy = R.equals(true);
R.all(isTruthy)([var1, var2, var3, var4]);

Both


const min = R.gte(R.__, 0);
const max = R.lte(R.__, 100);
const isPercent = R.both(min, max);
isPercent(10); // true
isPercent(-1); // false
isPercent(101); // false

Both


const notCat = (x) => x !== 'dog';
const notDog = (x) => x !== 'cat';

const isNotCatAndSeal = R.both(notCat, notDog);

isNotCatAndSeal('seal'); // true
isNotCatAndSeal('dog'); // false

allPass


const isDeveloper = R.propEq('role', 'developer');
const isLocatedWroclaw = R.propEq('location', 'Wroclaw');
const isGonnaBeHired = R.allPass(
	[isDeveloper, isLocatedWroclaw]
);

isGonnaBeHired({role: 'Developer', location: 'Berlin'});
//=> false
isGonnaBeHired({role: 'Developer', location: 'Wroclaw'});
//=> true

Apply


const maxNum = R.apply(Math.max, R.__);
maxNum([1, 5, 3]); // 5
maxNum([9, 9, 3]); // 9

Deep clone, merge

countBy


const numbers = [1.0, 1.1, 2.0, 3.0, 2.2];
R.countBy(Math.floor)(numbers);
//=> {'1': 2, '2': 2, '3': 1}

const letters = ['a', 'b', 'A'];
R.countBy(R.toLower)(letters);
//=> {'a': 2, 'b': 1}

defaultTo


const valOrNa = R.defaultTo('n/a');

defaultTo42(null);  //=> 'n/a'
defaultTo42(undefined);  //=> 'n/a'
defaultTo42('Hey there!');  //=> 'Hey there!'
defaultTo42(NaN); //=> 'n/a'

DropRepeats 😉


R.dropRepeats([1, 1, 2, 4, 4, 2]); //=> [1, 2, 4, 2]

eqProps


const o1 = { a: 3, b: 2, c: 3 };
const o2 = { a: 1, b: 'c', c: 3 };
R.eqProps('a', o1, o2); //=> false
R.eqProps('c', o1, o2); //=> true

Deeper

https://www.reddit.com/r/ProgrammerHumor/comments/6d3eqh/functional_programming_for_beginners/

Evolve


const obj = {
	firstName: '  johny ',
	data: {
		elapsed: 100,
		remaining: 1400
	},
	id:123
};



const transformations = {
  firstName: R.pipe(R.trim, R.toUpper),
  lastName: R.trim,
  data: {
  	elapsed: R.add(1),
  	remaining: R.add(-1)
  }
};

R.evolve(transformations, obj);

Lenses

Inner Join


R.innerJoin(
  (record, id) => record.id === id,
  [{id: 824, name: 'Richie Furay'},
  {id: 956, name: 'Dewey Martin'},
  {id: 313, name: 'Bruce Palmer'},
  {id: 456, name: 'Stephen Stills'},
  {id: 177, name: 'Neil Young'}],
  [177, 456, 999]
);
//=> [
	{id: 456, name: 'Stephen Stills'},
	{id: 177, name: 'Neil Young'}
]

Project


const user1 = {
	name: 'Jack',
	comments: 50,
	posts: 1
};
const user2 = {
	name: 'Fred',
	comments: 12,
	posts: 14
};

R.project(['name', 'posts'], [user1, user2]); //=> 
[{name: 'Jack', posts: 1}, {name: 'Fred', posts: 14}]


R.project(['name', 'posts'], [user1, user2]);


SELECT name, posts FROM users;

Path 😍 (kinda Elvis Operator)


R.path(['a', 'b'], {a: {b: 2}});

PathOr


R.pathOr('N/A', ['a', 'b'], {a: {b: 2}});

Pipes

Pipe


const data = [
 {a: { b: 5.12 } },
 {a: { b: 7.1102 } }
];
const dataDiss = R.pathOr(0, ['a', 'b'], R.__);
const test1 = dataDiss({a: { b: 5.12 }});
const dataPipe = R.pipe(dataDiss, Math.floor, x => x * 2);
const res = data.map(dataPipe); // [10, 14]

Difference?


const dataPipe = R.pipe(dataDiss, Math.floor, x => x * 2);
const res = data.map(dataPipe);
// [10, 14]

const res = data
	.map(dataDiss)
	.map(Math.floor)
	.map(x => x * 2);
// [10, 14]

R.propOr


const alice = {
  name: 'ALICE',
  age: 101
};
const favorite = R.prop('favoriteLibrary');
const favoriteWithDefault = 
	R.propOr('Ramda', 'favoriteLibrary');

favorite(alice);  //=> undefined
favoriteWithDefault(alice);  //=> 'Ramda'

Flatten


R.flatten([1, 2, [3, 4], [5, [6, [7, 8], 9]]]);
// [1, 2, 3, 4, 5, 6, 7, 8, 9];

R.flatten ~ _.flattenDeep

Overlaps with lodash / underscore

Questions?

Send me a Pigeon