Is there a name for the function that returns a positionally-expanding version of its argument?

StackOverflow https://stackoverflow.com/questions/9439117

  •  12-11-2019
  •  | 
  •  

Question

Consider splatter in this Python code:

def splatter(fn):
    return lambda (args): fn(*args)

def add(a, b):
    return a + b

list1 = [1, 2, 3]
list2 = [4, 5, 6]
print map(splatter(add), zip(list1, list2))

Mapping an n-ary function over n zipped sequences seems like a common enough operation that there might be a name for this already, but I have no idea where I'd find that. It vaguely evokes currying, and it seems like there are probably other related argument-centric HOFs that I've never heard of. Does anyone know if this is a "well-known" function? When discussing it I am currently stuck with the type of awkward language used in the question title.

Edit

Wow, Python's map does this automatically. You can write:

map(add, list1, list2)

And it will do the right thing, saving you the trouble of splattering your function. The only difference is that zip returns a list whose length is the the length of its shortest argument, whereas map extends shorter lists with None.

Was it helpful?

Solution 2

I randomly saw this in my list of "Questions asked," and was surprised that I now know the answer.

There are two interpretations of the function that I asked.

The first was my intent: to take a function that takes a fixed number of arguments and convert it into a function that takes those arguments as a fixed-size list or tuple. In Haskell, the function that does this operation is called uncurry.

uncurry :: (a -> b -> c) -> ((a, b) -> c)

(Extra parens for clarity.)

It's easy to imagine extending this to functions of more than two arguments, though it can't be expressed in Haskell. But uncurry3, uncurry4, etc. would not be out of place.

So I was right that it "vaguely evokes currying," as it is really the opposite.


The second interpretation is to take a function that takes an intentionally variable number of arguments and return a function that takes a single list.

Because splat is so weird as a syntactic construct in Python, this is hard to reason about.

But if we imagine, say, JavaScript, which has a first-class named function for "splatting:"

varFn.apply(null, args)

var splatter = function(f) {
    return function(arg) {
        return f.apply(null, arg);
    };
};

Then we could rephrase that as merely a partial application of the "apply" function:

var splatter = function(f) {
    return Function.prototype.apply.bind(f, null);
};

Or using, Underscore's partial, we can come up with the point-free definition:

var splatter = _.partial(Function.prototype.bind.bind(Function.prototype.apply), _, null)

Yes, that is a nightmare.

(The alternative to _.partial requires defining some sort of swap helper and would come out even less readable, I think.)

So I think that the name of this operation is just "a partial application of apply", or in the Python case it's almost like a section of the splat operator -- if splat were an "actual" operator.


But the particular combination of uncurry, zip, and map in the original question is exactly zipWith, as chris pointed out. In fact, HLint by default includes a rule to replace this complex construct with a single call to zipWith.


I hope that clears things up, past Ian.

OTHER TIPS

I think zipWith is the function that you are searching (this name is at least used in Haskell). It is even a bit more general. In Haskell zipWith is defined as follows (where the first line is just the type):

zipWith :: (a -> b -> c) -> [a] -> [b] -> [c]
zipWith f (a:as) (b:bs) = f a b : zipWith f as bs
zipWith _ _      _      = []

And your example would be something like

zipWith (+) [1, 2, 3] [4, 5, 6]

Since I do not know python very well I can only point to "zipWith analogue in Python?".

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top