Thread Tools Display Modes
11-17-07, 10:06 PM   #1
Eidolarr
An Aku'mai Servant
 
Eidolarr's Avatar
AddOn Author - Click to view addons
Join Date: Jan 2006
Posts: 34
Nested '...' usage, currying?

Hey, for giggles I was trying to write a curry function. It looked something like this
Code:
function curry(f, ...)
  return function (..)
    f(..., ..)
  end
end
where the two dots are a different set of variable arguments from the three dots. So if I could define a channel greeting function as follows:
Code:
GreetChannel =  curry(SendChatMessage, "Hello, channel!", "CHANNEL", GetDefaultLanguage("player"))
so that
Code:
GreetChannel(1)
greets my channel 1 with "Hello, channel!". Is there any way for Lua to distinguish between differing sets of variable arguments?

I can't even seem to get this to work! I assume for the same reason
Code:
function curry(f, x, ...)
  if x or select("#", ...) > 0 then
    return curry(function(...) f(x, ...) end, ...)
  else
    return f
  end
end
I read the lua-users wiki article on currying, but it's not quite as general as the form of currying I am seeking. Any insight?

Last edited by Eidolarr : 11-17-07 at 10:06 PM. Reason: title clarity
  Reply With Quote
11-20-07, 05:50 AM   #2
Shirik
Blasphemer!
Premium Member
WoWInterface Super Mod
AddOn Author - Click to view addons
Join Date: Mar 2007
Posts: 818
Originally Posted by Casull
Hey, for giggles I was trying to write a curry function. It looked something like this
Code:
function curry(f, ...)
  return function (..)
    f(..., ..)
  end
end
where the two dots are a different set of variable arguments from the three dots. So if I could define a channel greeting function as follows:
Code:
GreetChannel =  curry(SendChatMessage, "Hello, channel!", "CHANNEL", GetDefaultLanguage("player"))
so that
Code:
GreetChannel(1)
greets my channel 1 with "Hello, channel!". Is there any way for Lua to distinguish between differing sets of variable arguments?

I can't even seem to get this to work! I assume for the same reason
Code:
function curry(f, x, ...)
  if x or select("#", ...) > 0 then
    return curry(function(...) f(x, ...) end, ...)
  else
    return f
  end
end
I read the lua-users wiki article on currying, but it's not quite as general as the form of currying I am seeking. Any insight?
It won't work anything like what you want it to look like, for several reasons.

Reason 1: Tuples are immutable. You cannot put them together like you want to. Take the following example:

Code:
function foo(...)
   return ..., 4;
end
print(foo(1,2,3)); -- Outputs 1,4
The ... will be shortened to a single term.


Reason 2: ... is not a valid upvalue. You can only use it in the current context. take the following example:

Code:
function foo(...)
   local f = function() return ... end;  -- Error
   return f;
end

local bar = foo(1,2,3);
print(bar());
At the location marked "Error", the following results: "cannot use '...' outside a vararg function near '...'"

It follows from reason 2 that there should never be a case where you need a "second" ... tuple. It, concurrently, follows from reason 1 that even if you did, you would not be able to operate on it in the sense that you desired to in the first place. Your only real solution is to generate a table from the tuple in the form:

Code:
function foo(...)
   return {...};
end

local t1 = foo(1,2,3);
local t2 = foo(4,5,6);
-- Concatenate them
local t = {};
for _,v in ipairs(t1) do
   table.insert(t, v);
end

for _,v in ipairs(t2) do
   table.insert(t, v);
end

print(unpack(t)) -- Outputs 1,2,3,4,5,6
Do note, however, that this solution is not perfect. It will, for example, have flaws when iterated over if nils are present in the tuple. Concurrently, trailing nils will not be preserved. Still, it is the most efficient, effective solution to your problem.
__________________
たしかにひとつのじだいがおわるのお
ぼくはこのめでみたよ
だけどつぎがじぶんおばんだってことわ
しりたくなかったんだ
It's my turn next.

Shakespeare liked regexes too!
/(bb|[^b]{2})/

Last edited by Shirik : 11-20-07 at 10:36 AM.
  Reply With Quote
11-20-07, 11:00 PM   #3
Malreth
A Murloc Raider
AddOn Author - Click to view addons
Join Date: Jul 2005
Posts: 9
For completeness sake, I'll post the solution I worked out...
Code:
function append(a, n, b, ...)
	if n == 0 then
		return a
	else
		return b, append(a, n-1, ...)
	end
end

function curry(f, ...)
	local n, v = select("#", ...), {...}
	return function (x)
		return f(append(x, n, unpack(v, 1, n)))
	end
end

> p = curry(print, "apple", "banana", "clinton", nil)
> p("socrates")	--> apple	banana	clinton	nil	socrates
> p("lamp")	--> apple	banana	clinton	nil	lamp
Unlike the solution that I posted over on the Blizzard forums, this one correctly handles trailing nils and doesn't generate extra garbage (at the expense of some minor processing time for the recursion). Like the solution on the Blizzard forums, it simplifies the inner closure to a single argument (otherwise it's not really a curry).

Enjoy!

Last edited by Malreth : 11-22-07 at 12:09 AM.
  Reply With Quote

WoWInterface » Developer Discussions » Lua/XML Help » Nested '...' usage, currying?


Posting Rules
You may not post new threads
You may not post replies
You may not post attachments
You may not edit your posts

vB code is On
Smilies are On
[IMG] code is On
HTML code is Off