sanctuary-js / sanctuary

:see_no_evil: Refuge from unsafe JavaScript
https://sanctuary.js.org
MIT License
3.03k stars 94 forks source link

Variadic uncurry #568

Closed masaeedu closed 5 years ago

masaeedu commented 6 years ago

A nice utility to have would be a simple uncurry function that takes a curried function of an arbitrary number of arguments and an array consisting of a corresponding number of values, and applies the latter to the former. The implementation could be:

// essentially S.reduce(S.I), but that doesn't pass Sanctuary's typechecker
const uncurry = f => arr => arr.reduce((p, c) => p(c), f)
uncurry(S.add)([1, 2]) // => 3

This would help clean up some parens noise in certain places, and works well with things like the pipeline operator; i.e. [2, 2] |> uncurry(S.add) // => 4.

davidchambers commented 6 years ago

Thumbs down from me, for two reasons:

masaeedu commented 6 years ago

@davidchambers I'm not sure if "nasty" functions here are ones that accept multiple arguments (i.e. Function.prototype.length > 1), as is the case with the input to curry. If so, the function produced by uncurry does not qualify, because it still only accepts a single argument.

Regarding heterogeneous arrays, there's already a couple of fairly essential convenience functions that work with heterogeneous arrays (pipe, pipeK), without which code quickly turns into a muddle of parentheses and temporary variables. uncurry is just another function for facilitating working with the library under JS's syntax constraints, although it is admittedly of more niche utility than the other two.

davidchambers commented 6 years ago

[T]he function produced by uncurry […] still only accepts a single argument.

Thanks for the correction, Asad.

You're right to point out that pipe and pipeK accept heterogeneous arrays. I'm reluctant to let this looseness spread. ;)

rockymadden commented 6 years ago

I have this exact function in my codebases, called apply, as a utility. I see the points @davidchambers makes and they make sense as an argument for lack of inclusion into sanctuary itself.

masaeedu commented 5 years ago

@davidchambers I think the reason this sort of thing is seen as "loose" is because of lack of expressiveness in the type system. We can imagine sound types for these functions that would work in dependently languages like Idris (and indeed, JS :wink:):

// :: type Curried []         r = r
// ::      Curried [x, ...xs] r = x -> Curried xs r

// :: Curried a r -> a -> r
const uncurry = f => args => args.reduce((p, c) => p(c), f)

// :: type Length []         = 0
// ::      Length [x, ...xs] = 1 + Length xs

// :: Length a -> (a -> r) -> Curried a r
const curry = n => f => {
  const rec = a => acc => (a === 0 ? f(acc) : x => rec(a - 1)([...acc, x]))
  return rec(n)([])
}