Skip to content

Instantly share code, notes, and snippets.

@gunar
Last active November 11, 2019 22:20
Show Gist options
  • Star 6 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save gunar/1268c997ca66343f060dbca07aee67bd to your computer and use it in GitHub Desktop.
Save gunar/1268c997ca66343f060dbca07aee67bd to your computer and use it in GitHub Desktop.
Currying Functions with Named Parameters in JavaScript
/**
* Currying Functions with Named Parameters.
* @gunar, @drboolean, @dtipson 2016
*
* Why does it return a thunk, though?
* Because in JS named arguments are opaque. There is no way of getting a function's named arguments list.
* e.g.
* const foo = function ({ a, b }) { }
* console.log(foo.arguments) // Throws. Ideally would return ['a', 'b'].
*
* What would be an alternative?
* Providing a "spec". curryNamed would have to be told what the named arguments were upfront
* (as curryN does with numbered arguments). I personally think that's less useful.
*/
'use strict'
function curryNamed(fn, acc = {}) {
return args => {
if (!args) return fn(acc)
return curryNamed(fn, Object.assign({}, acc, args))
}
}
const foo = ({ a, b, c } = {}) => console.log({ a, b, c })
const bar = curryNamed(foo)
bar() // { a: undefined, b: undefined, c: undefined }
const a = bar({ a: 1 })
a() // { a: 1, b: undefined, c: undefined }
const ab = a({ b: 2 })
ab() // { a: 1, b: 2, c: undefined }
bar({ a: 1, b: 2, c: 3 })() // { a: 1, b: 2, c: 3 }
@gunar
Copy link
Author

gunar commented Sep 2, 2016

NB: There's still room to make it context aware i.e. this.

@gunar
Copy link
Author

gunar commented Sep 2, 2016

Having to "spec" all arguments beforehand would suck

  const bar = curryNamed(foo, ['a', 'b', 'c'])

but there's a middle path. Using just a number, like curryN does.

'use strict'

function curryNamedN(fn, len = 0, acc = {}) {
  if (len < 1) return fn(Object.assign({}, acc))
  return (args = {}) => {
    const _len = len - Object.keys(args).length
    return curryNamedN(fn, _len, Object.assign({}, acc, args))
  }
}

// @gunar
const foo = ({ a, b, c } = {}) => console.log({ a, b, c })
const bar = curryNamedN(foo, 3)

bar() // undefined

const a = bar({ a: 1 }) // undefined
const ab = a({ b: 2 }) // undefined
ab({ c: 3 }) // { a: 1, b: 2, c: 3 }

bar({ a: 1, b: 2, c: 3 }) // { a: 1, b: 2, c: 3 }

@dtipson
Copy link

dtipson commented Sep 2, 2016

Is this desired though?

bar({a:6,b:2,z:7});//->Object {a: 6, b: 2, c: undefined}

If the number of props is all it knows, then there's nothing guaranteeing that the named props used in the function will all be there, so they can't be relied on either in the function or any functions this one is composed with.

@gunar
Copy link
Author

gunar commented Sep 14, 2016

You are correct. This should be the format:

const curried = curryNamed(fn, ['user', 'client']);
curried({ user: 1 });
curried({ client: 2 }) // evaluates "fn";

@gunar
Copy link
Author

gunar commented Jul 13, 2017

There y'all go

https://github.com/gunar/ncurry

It accepts partial application and stuff.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment