Fwd: How to forward declare globals

classic Classic list List threaded Threaded
14 messages Options
Reply | Threaded
Open this post in threaded view
|

Fwd: How to forward declare globals

Charles Smith
It's said that it's not necessary to have forward declarations, but then what's wrong with this:

    v = m1

    function m1 ()
        print ("hello world")
    end

    v()

But this works:

    function m1 ()
        print ("hello world")
    end

    v = m1

    v()

Reply | Threaded
Open this post in threaded view
|

Re: Fwd: How to forward declare globals

Rob Kendrick-2
On Mon, Oct 06, 2014 at 12:50:32PM +0200, Charles Smith wrote:

> It's said that it's not necessary to have forward declarations, but then
> what's wrong with this:
>
>     v = m1
>
>     function m1 ()
>         print ("hello world")
>     end
>
>     v()a

In the first line, you set v to m1 (which is nil).  Then you give m1 a
value.

Remember that:
        function m1()
is just sugar for
        m1 = function()

You wouldn't expect this to work, either:
        v = m1
        m1 = "foo"
        assert(v == m1)

B.

Reply | Threaded
Open this post in threaded view
|

Re: Fwd: How to forward declare globals

Scott Morgan
In reply to this post by Charles Smith
On 06/10/14 11:50, Charles Smith wrote:

> It's said that it's not necessary to have forward declarations, but then
> what's wrong with this:
>
>     v = m1
>
>     function m1 ()
>         print ("hello world")
>     end
>
>     v()
>
> But this works:
>
>     function m1 ()
>         print ("hello world")
>     end
>
>     v = m1
>
>     v()

You might be thinking about this in a 'C' way. That is, in C, regardless
of where a function is written in the source, it always exists and is
available from the start of execution (you just need a forward
deceleration to access it).

In Lua, the function only exists from the point the code is reached and
executed. Before that point there is no function.

As such, you don't forward declare functions like you do in C (or C++,
or various other statically compiled languages), you just pass the
function along to wherever it needs to be used, when it's needed, just
like any other variable.

Scott


Reply | Threaded
Open this post in threaded view
|

Re: Fwd: How to forward declare globals

Charles Smith
Okay, I got it to work:

v = "m1"

function m1 ()
    print ("hello world")
end

_G[v]()


On Mon, Oct 6, 2014 at 1:15 PM, Scott Morgan <[hidden email]> wrote:
On 06/10/14 11:50, Charles Smith wrote:
> It's said that it's not necessary to have forward declarations, but then
> what's wrong with this:
>
>     v = m1
>
>     function m1 ()
>         print ("hello world")
>     end
>
>     v()
>
> But this works:
>
>     function m1 ()
>         print ("hello world")
>     end
>
>     v = m1
>
>     v()

You might be thinking about this in a 'C' way. That is, in C, regardless
of where a function is written in the source, it always exists and is
available from the start of execution (you just need a forward
deceleration to access it).

In Lua, the function only exists from the point the code is reached and
executed. Before that point there is no function.

As such, you don't forward declare functions like you do in C (or C++,
or various other statically compiled languages), you just pass the
function along to wherever it needs to be used, when it's needed, just
like any other variable.

Scott



Reply | Threaded
Open this post in threaded view
|

Re: Fwd: How to forward declare globals

Rob Kendrick-2
On Mon, Oct 06, 2014 at 02:15:36PM +0200, Charles Smith wrote:
> Okay, I got it to work:
>
> v = "m1"
>
> function m1 ()
>     print ("hello world")
> end
>
> _G[v]()

I'm trying, and failing, to see a situation where this is the most
elegant solution.

Can't you just define the function first?  Calling functions by name
rather than value is fraught with horror.

B.

Reply | Threaded
Open this post in threaded view
|

Re: Fwd: How to forward declare globals

Charles Smith
Recursive design.


msg_union = {
  ["0"] = "alt1",
  ["1"] = "alt2",
  ...
}

function alt1 (body)
...
function alt2 (body)


ielen = _G[msg_union[tostring (selector)]] (buffer(offset), pinfo, tree)

Maybe there's a better way?  It looks like it's going to be slow.


The msg_union has to be defined before it can be used by the (sub)msgs.  But it needs access to the (sub)msgs.


On Mon, Oct 6, 2014 at 2:17 PM, Rob Kendrick <[hidden email]> wrote:
On Mon, Oct 06, 2014 at 02:15:36PM +0200, Charles Smith wrote:
> Okay, I got it to work:
>
> v = "m1"
>
> function m1 ()
>     print ("hello world")
> end
>
> _G[v]()

I'm trying, and failing, to see a situation where this is the most
elegant solution.

Can't you just define the function first?  Calling functions by name
rather than value is fraught with horror.

B.


Reply | Threaded
Open this post in threaded view
|

Re: Fwd: How to forward declare globals

Coda Highland
On Mon, Oct 6, 2014 at 5:30 AM, Charles Smith
<[hidden email]> wrote:

> Recursive design.
>
>
> msg_union = {
>   ["0"] = "alt1",
>   ["1"] = "alt2",
>   ...
> }
>
> function alt1 (body)
> ...
> function alt2 (body)
>
>
> ielen = _G[msg_union[tostring (selector)]] (buffer(offset), pinfo, tree)
>
> Maybe there's a better way?  It looks like it's going to be slow.
>
>
> The msg_union has to be defined before it can be used by the (sub)msgs.  But
> it needs access to the (sub)msgs.

Remember that functions are first-class citizens in Lua. Try this:

function alt1 (body)
...
function alt2 (body)

msg_union = {
    [0] = alt1,
    [1] = alt2
}

ielen = msg_union[selector](blah)

That is, you can pass around functions just like you can pass around
any other value. Tou can store them in tables.

For what it's worth, this is legal:

msg_union = {
    [0] = function(body) ... end,
    [1] = function(body) ... end
}

/s/ Adam

Reply | Threaded
Open this post in threaded view
|

Re: Fwd: How to forward declare globals

Enrico Colombini
In reply to this post by Charles Smith
On 06/10/2014 14.15, Charles Smith wrote:
> Okay, I got it to work:
>
> v = "m1"
>
> function m1 ()
>      print ("hello world")
> end
>
> _G[v]()

If I understand correctly what you would like to achieve:

------------------
   function Init()
       v = m1
   end

   function m1()
       print ("hello world")
   end

   Init()
------------------

This way, m1 is already defined when its value is used, because Init()
is called after all chunk-level (global) assignments, such as "function
m1" have been executed.

--
   Enrico

Reply | Threaded
Open this post in threaded view
|

Re: Fwd: How to forward declare globals

Charles Smith
In reply to this post by Coda Highland
I need to take the time to develop a clearer example, but I think the point is, alt1 and alt2 also use msg_union.  That's what I mean by recursive design.  It's a classic chicken/egg issue.

On Mon, Oct 6, 2014 at 2:35 PM, Coda Highland <[hidden email]> wrote:
On Mon, Oct 6, 2014 at 5:30 AM, Charles Smith
<[hidden email]> wrote:
> Recursive design.
>
>
> msg_union = {
>   ["0"] = "alt1",
>   ["1"] = "alt2",
>   ...
> }
>
> function alt1 (body)
> ...
> function alt2 (body)
>
>
> ielen = _G[msg_union[tostring (selector)]] (buffer(offset), pinfo, tree)
>
> Maybe there's a better way?  It looks like it's going to be slow.
>
>
> The msg_union has to be defined before it can be used by the (sub)msgs.  But
> it needs access to the (sub)msgs.

Remember that functions are first-class citizens in Lua. Try this:

function alt1 (body)
...
function alt2 (body)

msg_union = {
    [0] = alt1,
    [1] = alt2
}

ielen = msg_union[selector](blah)

That is, you can pass around functions just like you can pass around
any other value. Tou can store them in tables.

For what it's worth, this is legal:

msg_union = {
    [0] = function(body) ... end,
    [1] = function(body) ... end
}

/s/ Adam


Reply | Threaded
Open this post in threaded view
|

Re: Fwd: How to forward declare globals

Coda Highland
On Mon, Oct 6, 2014 at 5:50 AM, Charles Smith
<[hidden email]> wrote:
> I need to take the time to develop a clearer example, but I think the point
> is, alt1 and alt2 also use msg_union.  That's what I mean by recursive
> design.  It's a classic chicken/egg issue.

(Please don't top-post. I know it's the default in gmail. It's bad
list etiquette.)

That's actually not a problem at all! If msg_union is a global, it
just works out of the box. If it's a local, then just break it into
two lines:

local msg_union
msg_union = { ... }

Alternatively, if it reads better for you, you can write it this way:

local msg_union = {}
msg_union[0] = function(body) ... end
msg_union[1] = function(body) ... end

/s/ Adam

Reply | Threaded
Open this post in threaded view
|

Re: Fwd: How to forward declare globals

Scott Morgan
In reply to this post by Charles Smith
On 06/10/14 13:50, Charles Smith wrote:

> I need to take the time to develop a clearer example, but I think the
> point is, alt1 and alt2 also use msg_union.  That's what I mean by
> recursive design.  It's a classic chicken/egg issue.
>
> On Mon, Oct 6, 2014 at 2:35 PM, Coda Highland <[hidden email]
> <mailto:[hidden email]>> wrote:
>
>     On Mon, Oct 6, 2014 at 5:30 AM, Charles Smith
>     <[hidden email] <mailto:[hidden email]>>
>     wrote:
>     > Recursive design.
>     >
>     >
>     > msg_union = {
>     >   ["0"] = "alt1",
>     >   ["1"] = "alt2",
>     >   ...
>     > }
>     >
>     > function alt1 (body)
>     > ...
>     > function alt2 (body)
>     >
>     >
>     > ielen = _G[msg_union[tostring (selector)]] (buffer(offset), pinfo, tree)
>     >
>     > Maybe there's a better way?  It looks like it's going to be slow.
>     >
>     >
>     > The msg_union has to be defined before it can be used by the (sub)msgs.  But
>     > it needs access to the (sub)msgs.


-- value from somewhere
v = 1

-- pre-define a local table
local msg_union = {}

-- fill the local with a table of functions that can refer to itself
msg_union[1] = function()
  print("mu:1")
  msg_union[2]()
end

msg_union[2] = function()
  print("mu:2")
  msg_union[3]()
end

msg_union[3] = function()
  print("mu:3)
end

-- call based on value
msg_union[v]()


There's also the ':' syntactic sugar that allows you to call a function
in a table and pass the table as the first argument.

local tab = {
  f1 = function(self, val)  -- 'self' argument manually specified
    print("In f1")
    print("tab.v =", self.v)
    print("val =", val)
  end,

  v = 123,
}

function tab:f2(val)  -- 'self' argument is created here for you
  print("In f2")      --   with the ':' operator
  self:f1(val)
end,

tab.f1(tab, "test1")  -- manually passing the table

tab:f1("test2")  -- automatically passing it with the ':'
tab:f2("test3")  --   operator

Bottom line, avoid globals where possible, pass info via arguments.

Scott


Reply | Threaded
Open this post in threaded view
|

Re: Fwd: How to forward declare globals

Andrew Starks
In reply to this post by Charles Smith


On Mon, Oct 6, 2014 at 7:50 AM, Charles Smith <[hidden email]> wrote:
I need to take the time to develop a clearer example, but I think the point is, alt1 and alt2 also use msg_union.  That's what I mean by recursive design.  It's a classic chicken/egg issue.

On Mon, Oct 6, 2014 at 2:35 PM, Coda Highland <[hidden email]> wrote:
On Mon, Oct 6, 2014 at 5:30 AM, Charles Smith
<[hidden email]> wrote:
> Recursive design.
>
>
> msg_union = {
>   ["0"] = "alt1",
>   ["1"] = "alt2",
>   ...
> }
>
> function alt1 (body)
> ...
> function alt2 (body)
>
>
> ielen = _G[msg_union[tostring (selector)]] (buffer(offset), pinfo, tree)
>
> Maybe there's a better way?  It looks like it's going to be slow.
>
>
> The msg_union has to be defined before it can be used by the (sub)msgs.  But
> it needs access to the (sub)msgs.

Remember that functions are first-class citizens in Lua. Try this:

function alt1 (body)
...
function alt2 (body)

msg_union = {
    [0] = alt1,
    [1] = alt2
}

ielen = msg_union[selector](blah)

That is, you can pass around functions just like you can pass around
any other value. Tou can store them in tables.

For what it's worth, this is legal:

msg_union = {
    [0] = function(body) ... end,
    [1] = function(body) ... end
}

/s/ Adam




I think that this is what you want:

```
local msg_union = {}

function msg_union:alt1(body)
  assert(self == msg_union)
  assert(self.alt2)
-- stuff
end

function msg_union:alt2(body)
--stuff
end

msg_union:alt() --does not fail

```

Am i missing something?

-Andrew
Reply | Threaded
Open this post in threaded view
|

Re: Fwd: How to forward declare globals

Charles Smith
okay, I've got to digest those.

On Mon, Oct 6, 2014 at 3:18 PM, Andrew Starks <[hidden email]> wrote:


On Mon, Oct 6, 2014 at 7:50 AM, Charles Smith <[hidden email]> wrote:
I need to take the time to develop a clearer example, but I think the point is, alt1 and alt2 also use msg_union.  That's what I mean by recursive design.  It's a classic chicken/egg issue.

On Mon, Oct 6, 2014 at 2:35 PM, Coda Highland <[hidden email]> wrote:
On Mon, Oct 6, 2014 at 5:30 AM, Charles Smith
<[hidden email]> wrote:
> Recursive design.
>
>
> msg_union = {
>   ["0"] = "alt1",
>   ["1"] = "alt2",
>   ...
> }
>
> function alt1 (body)
> ...
> function alt2 (body)
>
>
> ielen = _G[msg_union[tostring (selector)]] (buffer(offset), pinfo, tree)
>
> Maybe there's a better way?  It looks like it's going to be slow.
>
>
> The msg_union has to be defined before it can be used by the (sub)msgs.  But
> it needs access to the (sub)msgs.

Remember that functions are first-class citizens in Lua. Try this:

function alt1 (body)
...
function alt2 (body)

msg_union = {
    [0] = alt1,
    [1] = alt2
}

ielen = msg_union[selector](blah)

That is, you can pass around functions just like you can pass around
any other value. Tou can store them in tables.

For what it's worth, this is legal:

msg_union = {
    [0] = function(body) ... end,
    [1] = function(body) ... end
}

/s/ Adam




I think that this is what you want:

```
local msg_union = {}

function msg_union:alt1(body)
  assert(self == msg_union)
  assert(self.alt2)
-- stuff
end

function msg_union:alt2(body)
--stuff
end

msg_union:alt() --does not fail

```

Am i missing something?

-Andrew

Reply | Threaded
Open this post in threaded view
|

Re: How to forward declare globals

Tim Hill
In reply to this post by Charles Smith

On Oct 6, 2014, at 5:50 AM, Charles Smith <[hidden email]> wrote:

I need to take the time to develop a clearer example, but I think the point is, alt1 and alt2 also use msg_union.  That's what I mean by recursive design.  It's a classic chicken/egg issue.

On Mon, Oct 6, 2014 at 2:35 PM, Coda Highland <[hidden email]> wrote:
On Mon, Oct 6, 2014 at 5:30 AM, Charles Smith
<[hidden email]> wrote:
> Recursive design.
>
>
> msg_union = {
>   ["0"] = "alt1",
>   ["1"] = "alt2",
>   ...
> }
>

A couple of things to consider..

First, are you SURE about those table keys? [“0”] is different from [0] when indexing a table (the first indexes by a string that just happens to look like a number).

Second, you can break the recursion by understanding that your functions CAN reference msg_union before it is defined. So this will work:

function foo()
local x = msg_union[1]
x()
end

function bar()
print(“bar”)
end

msg_union = {
[0] = foo,
[1] = bar
}

foo()

The reason this works is that every reference to a global in Lua is converted into a table lookup that happens at run-time. So the compiler will re-write the first line of foo() as follows:

local x = (_G[“msg_union”])[1]

So it’s only necessary for msg_union to be valid when the functions are CALLED, not when the functions are DEFINED. This breaks your recursion cleanly. Note: Technically, the compiler actually generates slightly different code to what I showed, but I didn’t want to complicate the example. For reference, the exact code the compiler generates uses _ENV in place of _G, but in your example the two are equivalent.

—Tim