1. Nested functions - better example?

Good afternoon all, prepare youself for a techie bamboozle!

Nearly four years ago, I spent a sunday afternoon (about 6 hours) partially implementing nested functions, and it has remained in that partially broken state ever since. While it might be nice to solve the funarg problem and have proper closures (the ability for e() below to reference/update c and d even after b() has left the building) but I have to accept that's never going to happen without a drastic replacement of the whole call stack mechanism and all of our beloved pass by reference/copy-on-write semantics.

The better approach (for Phix) is to spot any attempt to write closure-like code and issue an error. [DONE, see [1]] In the following, e() must instead be explicitly passed anything it needs (such as c and/or d). Just this week, I suddenly realised that we could pass reference types to e(), namely a dictionary or a class instance, and it could update those directly, as opposed to the more traditional types atom/integer/string/sequence, which it can modify and return, but not update directly (cow-semantics). Anyway, I've hit a bit of a snag trying to clearly document these concepts (no s**t sherlock).

The following illustrates (hah!) nested scope rules:

object a = 1 
function b(object c) 
    object d = 2 
    function e(object f) 
        object g = 3 
        -- a,b,e,f,g in scope 
        -- c not in scope (except as a named parameter) 
        -- d not in scope 
--      ?c -- error -- 18,11,14,6   -- see note [1] 
--      ?f -- 18,11,14,6 
--      ?d -- error -- 3,3,3,3(!!)  -- see note [1] 
--      ?g -- 3,3,3,3 
        return {"a",a,"b45",iff(f==11?b(c:=4):5),"e67",iff(f==11?e(6):7),"f",f,"g",g} 
    end function 
    -- a,b,c,d,e in scope 
    -- f not in scope (except as a named parameter) 
    -- g not in scope 
    --?f -- error 
    --?g -- error 
--  ?c -- 1,8,4 
    return {"a",a,"b89",iff(c==1?b(8):9),"c",c,"d",d,"e",e(f:=c+10)} 
end function 
-- a,b in scope 
-- c not in scope (except as a named parameter) 
-- d,e,f,g not in scope 
pp(b(c:=a)) -- fine 
-- the output, albeit somewhat meaningless, is: 
--  {`a`, 1, `b89`, 
--   {`a`, 1, `b89`, 9, `c`, 8, `d`, 2, `e`, 
--    {`a`, 1, `b45`, 5, `e67`, 7, `f`, 18, `g`, 3}}, `c`, 1, `d`, 2, `e`, 
--   {`a`, 1, `b45`, 
--    {`a`, 1, `b89`, 9, `c`, 4, `d`, 2, `e`, 
--     {`a`, 1, `b45`, 5, `e67`, 7, `f`, 14, `g`, 3}}, `e67`, 
--    {`a`, 1, `b45`, 5, `e67`, 7, `f`, 6, `g`, 3}, `f`, 11, `g`, 3}} 
--?c -- error 
--?d -- error 
--?e -- error 
--?f -- error 
--?g -- error 

[1] I have now added the necessary hideScope/restoreScope to make c,d invisible from e(), though of course that's in pre-0.8.3 and has not been released yet. On 0.8.2 the first ?c will actually give the same results as ?f, since c and f match in a stack-frame-relative-wise way, ditto d and g.

I will just say this: I've used closures in JavaScript quite a bit recently, and they are pretty neat, especially if you've got no other way to handle them. The trouble is they are "hidden magic". There is absolutely nowhere, for instance, that I could ever output closure values in an ex.err. At best they would be an anonymous automatic construct from some point in a now destroyed stack. And that in turn would mean they are virtually completely and utterly impossible to ever debug. And you know me, I have no patience for any "waaah, I have to type it in, waaahh" programmers (granted for many that prolly stems from the 88,000 lines of boilerplate needed to start an empty project).

OK, back to the question at hand: The problem is simply that the above is (too stress-testy and) totally abstract, and therefore totally meaningless, and almost impossible to relate to any real-world use. Can anyone do better?

What might you use nested functions for, and would you cope, or cry like a baby 'cos you can't reference c and d?

new topic     » topic index » view message » categorize

2. Re: Nested functions - better example?

According to this a closure is a persistent local variable scope. For Orac what I did was this:

function f() 
  static int x = 2 // persistent var here initialised at runtime 
  int y = x + 1 
  return y 
end function 

The code "compiles" to:

int x = 2 // only accessed by the function 
function f() 
  int y = x + 1 
  return y 
end function 

That gives me persistent private variables. It would, of course, end up being shared across multiple calls to the function. But I never needed anything smarter than that. Or nested functions.

Spock

new topic     » goto parent     » topic index » view message » categorize

3. Re: Nested functions - better example?

Spock said...

For Orac what I did was this:

That actually went into 0.8.2

Spock said...

But I never needed anything smarter than that. Or nested functions.

So true. As it turns out, the version of "nested function" I have ended up with is almost exactly the same as static vars, like the nested function has been quietly "hoisted" out to just before the containing function, but remains totally invisible to the rest of the world. Overall I'm quite pleased with it, instead of saving a closure as a function with some "magic hidden variables", you (will) have to explicitly save {dictionary,function} or {instance,function} - for a tiny bit more verbosity you can inspect it, debug it, dump it, and fiddle with it to your heart's content. But yeah, now I've put it in, at a grand cost of about 10 hours, not counting much needlless fretting about it without ever doing anything else between part1 and part2, what am I ever going to use it for? smile UPDATE: answered myself, see post #12

new topic     » goto parent     » topic index » view message » categorize

4. Re: Nested functions - better example?

Edited.

Kat

new topic     » goto parent     » topic index » view message » categorize

5. Re: Nested functions - better example?

I got lost in your Rosetta example "Nested Function"

How about:

function makeList( sequence item,  string separator=". "  ) 
 
            function makeItem( integer count, string separator, string element ) 
                return  sprintf( "%d%s%s", {count,separator,element} ) 
            end function 
 
    for i=1 to length(item) do 
        item[i] = makeItem( i, separator, item[i] ) 
        end for 
 
    return item 
    end function 
 
? makeList( {"first","second","third"} ) 
 
--> {"1. first","2. second","3. third"} 

Used stuff like this in Pascal in the olden days.

be well
_tom

new topic     » goto parent     » topic index » view message » categorize

6. Re: Nested functions - better example?

I used nested functions when i could not goto cleanup code in a procedure, and copy/pasting cleanup code multiple times would break the alloted 64k of ram for a procedure.

procedure x 
-- declare local vars 
 
function cleanup 
-- process local vars 
end function -- cleanup 
 
-- program x code 
if blarg then { cleanup | return } 
-- more program x code 
if fubar'd, then { cleanup | return } 
-- more program x code 
cleanup 
end procedure -- x 

With goto:

procedure x 
-- declare local vars  
  
-- program x code  
if blarg then goto cleanup  
-- more program x code  
if fubar'd, then goto cleanup 
-- more program x code  
 
-- cleanup 
-- process local vars 
 
end procedure -- x  
So sure, in the philosophy of no goto, this gives us yet another way to write convoluted code.

Kat

new topic     » goto parent     » topic index » view message » categorize

7. Re: Nested functions - better example?

Hi

Call me thick if you want, but what is the advantage of a nested function, as opposed to an external function?

Cheers

Chris

new topic     » goto parent     » topic index » view message » categorize

8. Re: Nested functions - better example?

The inner functions have access to the outer function's variables.

In the olden daze, if the inner and outer functions both fit in the same 64k bank of ram, it saved a bank switch and other overhead. So if the inner function was called many times, not bank switching saved a lot of time.

Edit: also, pretty much the only difference tween a program and a bank allocation for a procedure was the header on the bank. So it was sometimes handy to overwrite a header so machine code could be placed there (i used it for device drivers). Way back when, i hoped such shenanigans could be used in Euphoria to implement an eval(), because i didn't know at the time about the intermediate il code.

Kat

new topic     » goto parent     » topic index » view message » categorize

9. Re: Nested functions - better example?

Ok, I see, so is there a real advantage on modern computers?

new topic     » goto parent     » topic index » view message » categorize

10. Re: Nested functions - better example?

I have not found a requirement to nest functions in the 20+ years i have known Euphoria.

But i have not run time profile tests for an lot of inlined code blocks vs repeatedly calling a nested function in Euphoria, because Euphoria doesn't allow it. If Pete does test this, i'd be interested in the numbers.

I think the variable scoping benefit went out the window with local scope restricted to an include file.

And OE doesn't run on DOS.

Kat

new topic     » goto parent     » topic index » view message » categorize

11. Re: Nested functions - better example?

katsmeow said...

The inner functions have access to the outer function's variables.

WHOAH! Alas not, since that would require a closure. Let me explain.

Bear in mind I've only just changed this, post-0.8.2, so all is not set in stone.

This summarises the current state of play (note I've just found some "static" issues, sidestepped here):

integer a = 'a' 
procedure p(integer b = 'b') 
    static integer c = 'c' 
    constant d = 'd' 
    integer e = 'e' 
     
    function f(integer g = 'g') 
        static integer h = 'h'  -- see PS 
        constant i = 'i'        --   "" 
        string res = "" 
        res &= a 
--      res &= b    -- undefined 
        res &= c 
        res &= d 
--      res &= e    -- undefined 
        res &= g 
        res &= h 
        res &= i 
        return res 
    end function 
 
    ?f() 
end procedure 
p() 

The output is "acdghi". I've actually just modified the new hideScope() routine mentioned at the top of this thread to leave static and local(private) constants visible.

So, why are b and e "undefined"?
The reason is they are not in the current call stack frame. While in a simple case like the above they are only one "hop" away, that would not be true for any nested recursive routines.
Also, imagine that you save routine_id("f") somewhere, and invoke it after p() has exited - b and e would then simply no longer exist, anywhere.

There is a reason why nested functions lay broken for four years, and that be it.

You can of course pass b and e explicitly to f(), and if you like return and store them.

Anyone thinking I am just being stubborn or making this stuff up is invited to read http://jibbering.com/faq/notes/closures/
I will not accept any responsibilty for or pay for the removal of any stains caused by liquified brain cells dribbling out of anyone's ears after reading that, however it is actually very well written.

PS: I may yet have to ban nested static/constants such as h and i above, hopefully not, or at least hopefully not a great loss.

new topic     » goto parent     » topic index » view message » categorize

12. Re: Nested functions - better example?

ChrisB said...

Call me thick if you want

I'm tempted blink

ChrisB said...

what is the advantage of a nested function, as opposed to an external function?

Consider this (although semi-based on something, it's actually complete fiction):

-- file pGUI.e 
function IupTreeView(...) 
    function createTVelem(...) 
    function iupTreeSetNodeAttributes(...) 
    function iupTreeAddNodesRec(...) 
    function deleteChildren(..) 
    function clickHandler(...) 

as compared to:

-- file pGUI.e 
function createTVelem(...) 
function iupTreeSetNodeAttributes(...) 
function iupTreeAddNodesRec(...) 
function deleteChildren(...) 
function clickHandler(...) 
function IupTreeView(...) 

Obviously, you're getting the same "breakdown into smaller more manageable units" value from both.
There's probably not much difference in understanding IupTreeView, but as a whole pGUI.e becomes far easier.
When modifying IupTreeView, in the second case you have to worry about/check whether anything else is using them,
whereas in the first case you can hack at them with impunity, only caring about a much smaller subset of pGUI.e
Some of them might obviously belong, but could you be so sure about deleteChildren() or clickHandler()?
There is nothing to stop you from having ten nested clickHandler()s, but you can only have one at top-level.

So, on the small scale and short include files the advantages are pretty much zero, but there's a gain when they grow.

new topic     » goto parent     » topic index » view message » categorize

13. Re: Nested functions - better example?

petelomax said...
katsmeow said...

The inner functions have access to the outer function's variables.

WHOAH! Alas not, since that would require a closure. Let me explain.

Bear in mind I've only just changed this, post-0.8.2, so all is not set in stone.

Alas yeas, it's why i wrote nested functions in Turbo Pascal on win3.0 way back when.

Of course, you can do it differently in Phix, doing so merely reduces (not eliminates, i suspect) the value in having such nests. I am not voting for or against it. Eu doesn't allow it either, and in Eu i'd just drop the "outer" and "inner" in their own include file to get fine-grained scoping, which i have done more than once. Not a problem.

Kat

new topic     » goto parent     » topic index » view message » categorize

Search



Quick Links

User menu

Not signed in.

Misc Menu