Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

varags functions that return vargas lambdas lua

If I want to write a function that takes varargs and returns a function that takes vargas I run into ambiguous ... for example

    function bind(func, ...) return function(...)  func(..., ...) end end
like image 303
Ace shinigami Avatar asked Mar 09 '23 07:03

Ace shinigami


2 Answers

First of all you're missing an end to close the bind function.

If you have ambiguities just resolve them by using different names.

function bind(func, ...)
  return function(...)  func(..., ...) end
end

If we test your code like that: bind(print, "a", "b", "c")(1,2,3)

you'll get the output:

1 1 2 3

If you have ... or any other name in a functions parameter list, that variable will be a local within the scope of that function. It will have priority over any other variable with the same name in a superior scope. So the ... in your anonymous function has nothing to do with the ... of function bind.

To resolve this problem you could simply do something like

function bind(func, ...)
  local a = table.pack(...)
  return function(...)  func(table.unpack(a, 1, a.n), ...) end
end

Now calling bind(print, "a", "b", "c")(1,2,3) will output:

a 1 2 3

To find out what happened to b and c please read this section: https://www.lua.org/manual/5.3/manual.html#3.4.11

(and the rest of Lua's manual of course)

When a function is called, the list of arguments is adjusted to the length of the list of parameters, unless the function is a vararg function, which is indicated by three dots ('...') at the end of its parameter list. A vararg function does not adjust its argument list; instead, it collects all extra arguments and supplies them to the function through a vararg expression, which is also written as three dots. The value of this expression is a list of all actual extra arguments, similar to a function with multiple results. If a vararg expression is used inside another expression or in the middle of a list of expressions, then its return list is adjusted to one element. If the expression is used as the last element of a list of expressions, then no adjustment is made (unless that last expression is enclosed in parentheses).

So something like func(..., ...) will never work, even if ... were two different lists.

To avoid this you have to contatenate both argument lists.

 function bind(func, ...)
  local args1 = table.pack(...)
  return function(...)
      local args2 = table.pack(...)

      for i = 1, args2.n do
          args1[args1.n+i] = args2[i]
      end
      args1.n = args1.n + args2.n

      func(table.unpack(args1, 1, args1.n))
  end
end

bind(print, "a", nil, "c")(1,nil,3)

Which finally gives us the desired output:

a nil c 1 nil 3

But I am sure you can think of a nicer way to achieve your goal without concatenating various varargs.

like image 98
Piglet Avatar answered Apr 01 '23 17:04

Piglet


You can try vararg library. Which also handles nil in arguments.

va = require "vararg"

function bind(func, ...)
  local args = va(...)
  return function(...)
      func(va.concat(args, va(...)))
  end
end

bind(print, 1, nil, 2)(3, nil, 5)
like image 29
moteus Avatar answered Apr 01 '23 15:04

moteus