This is a read-only snapshot of the ComputerCraft forums, taken in April 2020.
ProfessionalProcrastinator's profile picture

Why do functions need to be declared earlier in the code than the time they are referenced?

Started by ProfessionalProcrastinator, 22 November 2016 - 02:53 AM
ProfessionalProcrastinator #1
Posted 22 November 2016 - 03:53 AM
In my program TypeCC, I ended up using some recursive function calls. E.g., splitBlocks -> processBlockquote -> splitBlocks. When I initially tried this, I got an "attempt to call nil" error in the processBlockquote function. I fixed this by declaring "local splitBlocks" near the top of the file.

Why is this happening? I would understand if I was trying to call the function before it was defined, but not only is it not being called until after all function definition is taken place, it is being called from a function that it itself called…

Is this some weird voodoo with the local keyword, or something else?
Bomb Bloke #2
Posted 22 November 2016 - 05:04 AM
When Lua executes your script, it runs it left to right, top to bottom. When it hits a function definition, it's not built "as soon as the script starts" - it's built when execution actually gets to the line where you put the function definition. Heck, you can actually use the one set of function definitions to build multiple unique function pointers, if you want to (see the window API for an example of this - every time you call window.create(), a whole bunch of new function pointers get generated, dumped into a table, then passed back out as a new window object).

If your function refers to any variables (including ones holding more function pointers), Lua has to figure out which scope they exist in - it checks the current local scope first, then that of the next higher block, then the one above that, and so on. If no such variable already exists in any localised scope the current closure has access to, then it will build it to refer to the referenced variable in the global scope.

If you then later define that variable in the local scope, the first function won't find it in the global - it has to be declared locally beforehand, as a proper upvalue.

For example, if processBlockquote() is built with a reference to splitBlocks before splitBlocks() has been defined, processBlockquote() will assume splitBlocks exists in the global scope. If you later define splitBlocks in the local scope instead, processBlockquote() won't find it. If you declare splitBlocks as a local variable before building processBlockquote(), and then later construct the actual splitBlocks() function after processBlockquote() has been built, then everything is fine because processBlockquote() already knows the correct scope to look in.

In this case, there's no obvious reason why you couldn't just build the splitBlocks() function before processBlockquote(). It's a little trickier with classifyElements(), though…
Lupus590 #3
Posted 22 November 2016 - 09:47 AM
For example, if processBlockquote() is built with a reference to splitBlocks before splitBlocks() has been defined, processBlockquote() will assume splitBlocks exists in the global scope. If you later define splitBlocks in the local scope instead, processBlockquote() won't find it. If you declare splitBlocks as a local variable before building processBlockquote(), and then later construct the actual splitBlocks() function after processBlockquote() has been built, then everything is fine because processBlockquote() already knows the correct scope to look in.

this example as code:

--# won't work
local function processBlockquote()
  --# do stuff
  splitBlocks()
  --# do more stuff
end
local function splitBlocks()
  --# do stuff
end

--# will work
local splitBlocks --# this basically tells lua that there will be a thing called splitBlocks eventually
local function processBlockquote()
  --# do stuff
  splitBlocks()
  --# do more stuff
end
function splitBlocks()
  --# do stuff
end
Edited on 22 November 2016 - 09:39 AM
ProfessionalProcrastinator #4
Posted 23 November 2016 - 04:06 PM
Bomb Bloke, that makes perfect sense. I figured it was some weird scoping issue.

Lupus, I think maybe you misunderstood my predicament. It's more like this:


local function processBlockquote()
  # do stuff
  splitBlocks()
  # do stuff
end

local function splitBlocks()
  # do stuff
  processBlockquote()
  #do stuff
end
Exerro #5
Posted 23 November 2016 - 04:29 PM
Lupus' code would work in that situation too. You're referencing processBlockquote in splitBlocks() after it was defined, so there's no need to predeclare both of them.


local splitBlocks
local function processBlockquote()
  # do stuff
  splitBlocks()
  # do stuff
end

function splitBlocks()
  # do stuff
  processBlockquote()
  #do stuff
end

The syntax `local function f() … end` actually expands to something like this:

local f
f = function() ... end

That's why you can reference the function from within itself, as the local has been declared above where the function closure is created. It also means that, in the code example Lupus gave, both function variables were declared before either was defined.
Lupus590 #6
Posted 23 November 2016 - 06:29 PM
I only included the parts relevant to the issue, namely trying to call something which is defined after it was called. In my example the comments could be abstracting a call to processBlockquote.