Thats Amazing! (Closure)

General discussion about LÖVE, Lua, game development, puns, and unicorns.
Post Reply
Ekamu
Party member
Posts: 178
Joined: Fri Sep 20, 2013 11:06 am

Thats Amazing! (Closure)

Post by Ekamu »

closure
closure
closure.png (4.42 KiB) Viewed 2945 times
you can create a local variable and a function within its scope. Outside the scope of the local variable the functions will still hold reference to the local variable and even better with lexical scoping!

My God, Lua where have you been all my life!

So any of you guys know some cool ways I could use closure in a game?

or some simple examples would be awesome \(^m^)/
User avatar
Codex
Party member
Posts: 106
Joined: Tue Mar 06, 2012 6:49 am

Re: Thats Amazing! (Closure)

Post by Codex »

Why did you do, "return {get=get, set=set}"?
Ekamu
Party member
Posts: 178
Joined: Fri Sep 20, 2013 11:06 am

Re: Thats Amazing! (Closure)

Post by Ekamu »

Good question (^_^)

You need to access the functions

Code: Select all

local function get()
--code
end

local function set(new_v)
--code
end
outside the local function f()

what return {get = get, set = set} does is:
return a table with index get and value function get() and index set with value function set().

remember functions are first class values in Lua, this means we can treat them just like values and return them just like we would a normal value

{} is the table constructor
table = { index_a = "value 1", index_b = "value 2" }
is syntax sugar for
table = {}
table.index_a = "value 1" --same as table["index_a"] = "value 1"
table.index_b = "value 2"

so function f() returns the table with access to get() and set() within its scope

r = {} --think of r as a table
r.get = get --variable get = function()
r.set = set --variable set = function(new_v)

outside our function (f()) scope we can set multiple variables to call f()
local t,u = f(), f() --assign multiple variables, the () calls a function

and then access the local functions get() and set() with the returned table indexes that where stored in variables t,u
print(t.get())
print(u.get())

functions in Lua are also ambiguous which means they have no fixed name and by assigning variables t,u = f(),f() --multiple assignment
we are not copying the function f() or just storing its values but instead referring to it with the variables t and u.

--an example to demonstrate this
foo = print
foo('used just like print(...)")
==> used just like print(...)

notice how foo now has access to print. (infact print is just a name used to reffer to its ambigouse function in Lua)


One more thing, also notice how I used

do
--local scope
end

this is only for the interactive Lua shell, where every-line is its own scope unless you explicitly create a do end chunk. you don't need to worry about that in LOVE though bear in mind the scope in which your code is in.

You can learn more here:
http://lua-users.org/wiki/FunctionsTutorial
http://lua-users.org/wiki/ScopeTutorial
User avatar
markgo
Party member
Posts: 190
Joined: Sat Jan 05, 2013 12:21 am
Location: USA

Re: Thats Amazing! (Closure)

Post by markgo »

A use of closure: Put local variables at the top of your file. The variables are usually private constants or settings for your module. All your functions in that file will have access to the variables at the top once you call them.

Code: Select all

-- myfile.lua
local SOMETHING_COOL = 'cool'

function test()
 print(SOMETHING_COOL)
 SOMETHING_COOL = SOMETHING_COOL:rep(2)
end

-- anotherfile.lua
require 'myfile'
test() -- cool
test() -- coolcool
User avatar
Inny
Party member
Posts: 652
Joined: Fri Jan 30, 2009 3:41 am
Location: New York

Re: Thats Amazing! (Closure)

Post by Inny »

Optimization note: Lua's closures use more memory than a table+metatable does. That's because it closes over the entire stack-frame, and not the variables directly. So, if you're going to do a closure here and there, that's fine. If you're going to write a particle system with tens of thousands of concurrent entities each with it's own closure, you're going to see some pretty hard garbage collection cycling.
Ekamu
Party member
Posts: 178
Joined: Fri Sep 20, 2013 11:06 am

Re: Thats Amazing! (Closure)

Post by Ekamu »

o.k..

I get confused with closure in tail calls. would the particle system use closure if for example it was to run itself over and over for a certain time, and each time calling upvalues from within itself or is it only closure when upvalues are from somewhere completely different like another function...

so we wont use closure with recursion but its OK with normal tail calls and general code right?
User avatar
Inny
Party member
Posts: 652
Joined: Fri Jan 30, 2009 3:41 am
Location: New York

Re: Thats Amazing! (Closure)

Post by Inny »

Ekamu wrote:o.k..

I get confused with closure in tail calls. would the particle system use closure if for example it was to run itself over and over for a certain time, and each time calling upvalues from within itself or is it only closure when upvalues are from somewhere completely different like another function...

so we wont use closure with recursion but its OK with normal tail calls and general code right?
It sounds like I may have confused you a bit so I'll explain more.

Where ever you have the function keyword, you have a closure, which closes over the lexical environment that it appears in. So, lets look at this code:

Code: Select all

function createClosures()
  local t = {}
  for i = 1, 10 do
    t[#t+1] = function() return i end
  end
  return t
end
What happened here is I created 10 functions. Each function has a different value, and they all close over the variable "i". Interesting to note is that the for..do..end block in there creates a new variable i for each function created, so each function will return a different value. That's in stark contrast to how javascript works with the same concept.

So, to make this a bit more apparent, consider the particle thing I mentioned before:

Code: Select all

function createParticle()
  local self, x, y = {}, 0, 0
  function self:draw()
    -- drawing code here
  end
  function self:update(dt)
    -- updating code here
  end
  return self
end
particles = {}
for i = 1, 10000 do particles[i] = createParticle() end
What I just did there is create 10001 tables, 20000 variables, 10000 closures, and 20000 functions. By comparison, if I had used tables and metatables, I would have only created 10002 tables, 20000 variables, 1 closure, and 2 functions. Closures use more memory. They're great to use, but try not to use them inside loops for objects that you would have tons of.

Just for good measure, here's the lightest particle system you could make in Lua:
particles = { x={}, y={}, N=0 }

function particles.create()
particles.N = particles.N + 1
particles.x[particles.N] = 0
particles.y[particles.N] = 0
end

function particles.draw()
for i = 1, particles.N do
-- drawing code
end
end

function particles.update(dt)
for i = 1, particles.N do
-- updating code
end
end

for i = 1, 10000 do particles.create() end
Here I made 3 tables, 20002 variables, 3 functions, and either 1 or 3 closures (I'm not sure if lua 5.1 shares that lexical environment, but I know lua 5.2 does). This code tends to be a bit more messy, so you only use this style for things where performance is crucial.
User avatar
Roland_Yonaba
Inner party member
Posts: 1563
Joined: Tue Jun 21, 2011 6:08 pm
Location: Ouagadougou (Burkina Faso)
Contact:

Re: Thats Amazing! (Closure)

Post by Roland_Yonaba »

As a complementary reading on the topic, which illustrates nicely Inny's comments, see this article on OO with closures, and this benchmark.
Ekamu
Party member
Posts: 178
Joined: Fri Sep 20, 2013 11:06 am

Re: Thats Amazing! (Closure)

Post by Ekamu »

Thanks for the link and summary.
so basically in a numeric for loop each iteration creates a new closure, this is bad for something like a particle system where performance is crucial right?
this is the same for any loop, not just numeric for loops.

Also those links on OOP and closure are great but I'm not yet there. I still need to cover Meta-methods, Environments and Modules. I like learning things in order and step by step but I will definitely read more about OOP very soon.
Post Reply

Who is online

Users browsing this forum: No registered users and 2 guests