0.0.1 • Published 8 years ago

binderize v0.0.1

Weekly downloads
3
License
MIT
Repository
github
Last release
8 years ago

Binderize - Experimenting with function bind syntax

Interested in using the new function bind syntax but none of your functions are compatible? Binderize them!

Converting the lodash chain example

import { bindFirst } from 'binderize';
import _ from 'lodash';

const { sortBy, map, head } = _.mapValues(_, bindFirst);

var users = [
  { 'user': 'barney',  'age': 36 },
  { 'user': 'fred',    'age': 40 },
  { 'user': 'pebbles', 'age': 1 }
];

var youngest = users
  ::sortBy('age')
  ::map(o => o.user + ' is ' + o.age)
  ::head();
// → 'pebbles is 1'

The same example with Ramda

import { bindLast } from 'binderize';
import R from 'ramda';

const { sortBy, map, head } = R.map(bindLast, R);

var users = [
  { 'user': 'barney',  'age': 36 },
  { 'user': 'fred',    'age': 40 },
  { 'user': 'pebbles', 'age': 1 }
];

var youngest = users
  ::sortBy(o => o.age)
  ::map(o => o.user + ' is ' + o.age)
  ::head();
// → 'pebbles is 1'

Now you can use your favorite libraries and functions with this new syntax.

Binder API

Each binder takes a function and allows it to accept one of its arguments as its this context, instead of regularly passed argument. The rule of thumb is to use the binder that corresponds to the argument you want to be on the left of the bind (::).

bind

Converts a single argument function that accepts that argument as its this context.

const head = bind(list => list[0]);

[1, 2, 3]::head(); // 1

bindFirst

Converts a function to one where the this context is passed as the first argument to the wrapped function.

const append = bindFirst((list, a) => [...list, a]);

[1, 2, 3]::append(4); // [1, 2, 3, 4]

bindLast

Converts a function to one where the this context is passed as the last argument to the wrapped function.

const append = bindFirst((a, list) => [...list, a]);

[1, 2, 3]::append(4); // [1, 2, 3, 4]

bindN

Converts a function to one where the this context is passed as the Nth argument to the wrapped function.

const greet = bindN(1, (greeting, name, ending) => `${greeting} ${name}, ${ending}`);

"Adam"::greet("Hello", "how are you?"); // "Hello Adam, how are you?";

Modifiers - (Experimental)

Modifiers can be used to enable functions to work in more contexts than they were originally defined. You get a higher level of functionality with the same sugary function bind syntax. These modifiers can also be stacked to lift to combine these contexts. Import these from binderize/modifiers.

import { ... } from 'binderize/modifiers'

These are experimental, and I am not sure how useful they will be. Any ideas are welcome!

maybe

Allows a function to not be called when the payload is null or undefined to avoid exceptions

const upcase = x => x.toUpperCase();
const maybeUpcase = maybe::bind(upcase);

null::maybeUpcase() // null / Does not throw an error
"test"::maybeUpdate() // "TEST"

on

Allows a function to run with a certain field of an object as the payload.

const increment = x => x + 1;
const growOlder = on('age')::bind(increment);

const user = { name: 'Adam', age: 28 };
user::growOlder() // { name: 'Adam', age: 29 };

promise

Allows the function to treat the result of a promise as its payload.

const log = ::console.log;
const logPromise = promise::bind(log);

Promise.resolve(1)::logPromise(); // logs 1 when the promise resolves

map

Maps the function over the payload.

const increment = x => x + 1;
const incrementAll = map::bind(increment);

[1, 2, 3]::incrementAll() // [2, 3, 4]

Stacking / Chaining

As mentioned these can be changed. Imaging pulling a list of users back from an API, incrementing a nullable age variable (or leaving them null), then sending them back to the API. You can use a stack of modifiers to lift a simple function like increment into the context you want it work in.

const increment = x => x + 1;
// When the promise resolves (promise)
// for each value (map)
// on the age field (on)
// maybe (maybe) increment the value
const incrementUsers = promise::map::on('age')::maybe::bind(increment);

getUsersAsPromise()::incrementAllAges().then(saveUsers);

If our API returns this list of users:

[
  {
    "id": 1,
    "age": 10
  },
  {
    "id": 2,
    "age": null
  }
]

This updated list will be passed to the saveUsers function above:

[
  {
    "id": 1,
    "age": 11
  },
  {
    "id": 2,
    "age": null
  }
]

Stacking can also be used in other creative ways and even combined with the different apply functions in binderize (apply, applyFirst, applyLast, applyN) to create some other useful functionality.

safeThen implicitly handles null / undefined values in promises.

const safeThen = promise::maybe(apply);

Promise.resolve(null)::safeThen(x => x.toUpperCase()).then(x => console.log(x)); // logs null - Does not error
Promise.resolve("test")::safeThen(x => x.toUpperCase()).then(x => console.log(x)); // logs "TEST"

License

MIT