6

I have a function in search of a name.

I've been building a new functional programming library in Javascript, and I recently added a new function that looks useful to me. I named it useWith but I'm wondering if it's a function already known to functional programmers under a different name.

The function is related to compose in that it returns a new function that combines several existing ones, but in a slightly different manner than compose. The first parameter it receives is singled out; the remainder are treated uniformly. When the returned function is called, the arguments are passed respectively to each of these remaining functions, and the results, along with any unpaired arguments are sent to that first function, whose result is then returned. So if this is called with only two functions, and if the resulting function is passed a single argument, this is exactly equivalent to compose. But it has some additional features for multiple arguments.

The reason I wanted this function was that I was implementing something like the project function Michal Fogus presented in Functional Javascript, an equivalent of Codd's project for an array of similar Javascript objects, and similar to SQL's select verb. It is quite easy to write like this:

var project = curry(function(keys, table) {
    return map(pick(keys), table);
});


// Used like this:
var kids = [{name: 'Bob', age: 3, eyes: 'blue', hair: 'brown'}, 
            {name: 'Sue', age: 5, eyes: 'hazel', hair: 'blonde'}];
project(['name', 'eyes'], kids); 
//=> [{name: 'Bob', eyes: 'blue'}, {name: 'Sue', eyes: 'hazel'}]

But I really wanted to implement it in a points-free style. But of course this wouldn't work:

var project = compose(map, pick); // NO!

...because there is no facility to pass through the second parameter, table inside compose.

That's where this new function comes in:

var project = useWith(map, pick);

I made it more generic than this case, though. The original approach was called using with the parameters reversed, so that it read as a command: "Using pick, map". But that made it hard to extend to multiple parameters. I'd have to make the first one an array, or allow it to be either an array or a single function, and I didn't really want to go there. This seemed a much better solution.

I feel as though I can't be the first person with the need for a function like this. Is this a common pattern in FP languages? Is there a common name for this function? If not, are there suggestions for a better name than useWith?


If you're curious, here's the implementation of useWith, using a pretty obvious slice, and a fairly standard curry:

var useWith = curry(function(fn /*, tranformers */) {
    var tranformers = slice(arguments, 1);
    return function() {
        var args = [], idx = -1;
        while (++idx < tranformers.length) {
            args.push(tranformers[idx](arguments[idx]))
        }
        return fn.apply(this, args.concat(slice(arguments, tranformers.length)));
    };
});
dvlsg
  • 5,378
  • 2
  • 29
  • 34
Scott Sauyet
  • 49,207
  • 4
  • 49
  • 103
  • I'd say that `compose(map, pick)` is only a *NO* when `compose` feeds all the arguments of the result function into `pick` - which it not necessarily should. If you take the strict, fixed-arity version (like in Haskell `.`) it actually works. – Bergi Jul 01 '13 at 15:18
  • @Bergi: Yes, it took me a little while to realize that it wasn't going to work in Javascript's dynamic-arity functions. I scratched my head for a little while not able to see what could be wrong with this elegant little code! :-) I'm trying to decide if I would want to change `compose` in a way that would solve this problem. Right now it slurps up all its arguments, treating the first call as different from subsequent ones, which obviously only get one argument. – Scott Sauyet Jul 01 '13 at 15:55
  • I think neither is wrong or right. It's definitely worth investigating how function composition (and similar functional approaches) should work on variadic functions (and what possibilities for realisation exist), and finding standard terminology for them. – Bergi Jul 01 '13 at 16:04
  • @Bergi: I've recently given a short presentation on different variations on functional composition in Javascript: http://scott.sauyet.com/Javascript/Talk/Compose/ . There are surprisingly many different approaches one might take. – Scott Sauyet Jul 01 '13 at 17:21
  • 1
    Thanks for the link, quite interesting. But the talk seems to focus on [verbose] API, I've found only three different functionalities: [the naive one](http://scott.sauyet.com/Javascript/Talk/Compose/#slide-10), the [flaws-fixed](http://scott.sauyet.com/Javascript/Talk/Compose/#slide-14)-and-[multiple-arguments](http://scott.sauyet.com/Javascript/Talk/Compose/#slide-17) one, and [the reverse one](http://scott.sauyet.com/Javascript/Talk/Compose/#slide-25) ("`sequence`"). But there are other ways to fix those flaws you mentioned, like you now did in `useWith`… – Bergi Jul 01 '13 at 22:16
  • @Bergi: Yes, the focus of the talk was on API design, not on underlying functionality. The link in phg's answer has some interesting alternatives regarding the varieties of behaviors that might be considered regarding such variadic functions. – Scott Sauyet Jul 02 '13 at 00:35

1 Answers1

4

I could have misunderstood something due to lack of javascript knowledge, but if map is a curried function, and compose returns a curried function, then compose(map, pick) is project, since map is partially applied to pick -- and partial application works only on the first argument. Here's the proof of what I mean:

compose map pick =
(\f g x. f (g x)) map pick =         -- definition of compose
(\g x. map (g x)) pick =             -- apply to map
\x. map (pick x) =                   -- apply to pick
\x. (\y. map (pick x) y) =           -- eta-expansion of inner function
\key table. map (pick key) table     -- combine and rename

(I assumed you know lambda calculus based on the name of your library.)

As you can see, this doesn't depend on the arity of map -- you can eta-expand as often as you like, thus, no extra work is required for generalization. As long as everything is curried.

For having mixed-arity, curried and uncurried functions at the same time, there are combinators like these, but that's probably a bit too extreme.

phipsgabler
  • 20,535
  • 4
  • 40
  • 60
  • My version of compose is of dynamic arity in two ways. First it composes an arbitrary sequence of functions. Second, the resulting function feeds all its arguments into the first function in the chain. That second property is what's interfering here. (BTW, I can read the lambda expression, although my ramda and eweda library names are mostly a joke.) I will look at that link, too. Thanks for the explanation. I **knew** I wasn't too crazy! – Scott Sauyet Jul 01 '13 at 15:59