5

I think I have this down, but I'd like to confirm.

  1. With dynamic scope, it doesn't matter whether one uses a nested function or a separate function, as the variables simply depend on the call stack.

  2. With only pure functions involved, it also doesn't matter whether one uses a nested function or a separate function. This is true regardless of the type of scope.

  3. With lexical scope, nested functions roughly mimic calling a function with dynamic scope.

  4. With lexical scope, a program written entirely out of pure functions (perhaps allowing for a single impure print to the standard output) needs no garbage collecting. If it makes a difference, I am specifically thinking of GNU C with the nested function extension for this question.

NOTE: By pure function, I mean totally pure function: the only thing "read" is the parameters, the only thing "written" is the functions return.

Thanks

Ericson2314
  • 133
  • 7
  • Standard C does not have nested functions, so I've edited your title and tags! – Oliver Charlesworth May 23 '12 at 16:22
  • Thanks, though only for 4 am I really concerned with languages-specifics. For 1-3 I am wondering about lexical scoping in general, i.e. rules of thumb that ought to apply for any language that properly implements lexical scoping. So it's OK if there's exceptions. – Ericson2314 May 23 '12 at 18:41
  • Ah, ok. I assumed you were talking about C specifically, as it's the only language you'd mentioned. Feel free to adapt my title changes, etc. as necessary! – Oliver Charlesworth May 23 '12 at 18:44

2 Answers2

2

You have it right. I wouldn't try to put this into my brain as a case-by-case mnemo, though -- try to understand the whys, and you'll run into less surprises.

The documentation on the topic (at http://gcc.gnu.org/onlinedocs/gcc/Nested-Functions.html) is pretty good. If you know how the stack works in C, you should understand right away.

Also, a gift I found on the interwebs that may be useful, given your question:

#define lambda(type, body) ({ \
    type __anon_func__ body \
    __anon_func__; \
})

int (*foo) (double) = lambda(int, (double x) { return (int) x; });
salezica
  • 74,081
  • 25
  • 105
  • 166
  • interesting macro you have there. Where exactly does the underlying feature, __anon_func__, come from? I can't find it for some reason. – Ericson2314 May 23 '12 at 19:21
  • `Anon func` is the name I gave it. The two gcc features used there are compound statements and nested functions. – salezica May 24 '12 at 18:30
1

It would help if you had a specific language in mind, even though you tagged this with C and Lisp, that doesn't help tremendously without some concrete examples, as the two are pretty different. I don't think C is dynamically scoped at all, and there are numerous Lisps that support many combinations and variations of dynamic and lexical scoping.

  1. This is correct, but some languages complicate this by supporting both dynamic and lexical scope, allowing the programmer to specify which to use (Clojure and Common Lisp, for example).

  2. This is also correct. In this case what you're talking about is a function with no side effects and without any free variables.

  3. This isn't exactly true, it's more complicated than that. In a language with lexical scope that supports first-class functions and closures, a nested function will close over free variables that are bound in the environment it's defined in, and you could return that function to an outer scope, and those closed-over variables will be accessible and refer to the original scope they were defined in. Again, this is hard to talk about in the abstract without concrete examples from specific programming languages, so if you have something in mind, you should edit your question with code samples.

  4. I'm not sure what you mean by "garbage cleaning"? If you're referring to garbage collection as in automatic memory management, this is not correct in general, but I won't comment on the specific case of GNU C with nested functions, as I don't know exactly how that works.

okonomichiyaki
  • 8,355
  • 39
  • 51
  • I think he's talking about the GCC nested function extension – salezica May 23 '12 at 16:33
  • Yeah, I figured after the edits, but s/he also asked about dynamic scope which doesn't exist in C, does it? – okonomichiyaki May 23 '12 at 16:55
  • 3. Basically, it seams to me that **closing over** a variable has roughly the same effect as dynamic scoping, which i understand in the simplest case to involve only global variables which have new definitions pushed and popped as needed. Don't closures basically work in the same stack-based way? – Ericson2314 May 23 '12 at 18:55
  • 4. First of all, I do mean garbage collection [edited question to fix]. Well ignoring nested functions temporary, my understanding is that one only needs garbage collection to fix up inefficient manual memory allocation. However, if one's program is pure and blocked-based (not gotos or manual memory stuff--they're kind of too sides of the same coin IMO--and pure as in the pure functions I defined before), C's inbuilt functionality to handle structured programming should take care of everything. – Ericson2314 May 23 '12 at 19:03
  • Maybe in some very big functions or other blocks, garbage collecting could further save memory, but dividing those into smaller blocks should achieve the same effect. (Or since I am ignoring GNU nested functions for now, spiting functions into smaller functions rather than reorganizing them as as a series of small nested functions.) – Ericson2314 May 23 '12 at 19:07
  • 3. I think you still misunderstand exactly how lexical and dynamic scope differ. In a language that supports lexical closures, free variables in a function refer to the environment the function was defined in, while in a dynamically scoped language, free variables refer to the environment the function is called in. The implementation details are probably a bit hard to describe in a SO comment, but I wrote a snippet of Lisp that is valid Emacs Lisp and Common Lisp, to demonstrate the difference: https://gist.github.com/2782128 – okonomichiyaki May 24 '12 at 15:22
  • Hmm, well you are certainly right in that I would've assumed the behavior in both cases would be the same. With dynamic scoping, can global variables ever by redefined locally? – Ericson2314 May 24 '12 at 20:38
  • Sure, and [I added an example](https://gist.github.com/2782128). This isn't just an academic exercise either, there are practical uses of dynamic scoping. In CL and Clojure the global variable referencing the standard output port is a dynamic variable, and can be rebound to something else, allowing you to call other functions (possibly ones you didn't write) that in turn call standard IO functions, and capture the output to a string or to somewhere else. See CL's `with-output-to-string` or Clojure's `with-out-str`. – okonomichiyaki May 25 '12 at 16:30
  • Well, in your second revision, I would think there the code would behave the same in both emacs lisp and CL, because: 1. the parameter x would first override the global definition when return-a-nested-fn is *called* 2. the lambda is then evaluated with the new local x, 3. the function effectively returns (lambda () (list 'local-x y))) 4. the funcall simply calls the returned lambda function, it is ignorant of how it got there. – Ericson2314 May 26 '12 at 17:18
  • To summarize, if a function both defined and called in the same block, and read+writes only variables defined within itself and it's immediate parent block, it will behave the same regardless of whether dynamic scoping or static scoping is used. In your function, global x is always overwritten by the parameter x: Therefore the function only accesses variables defined within it; it is a pure function – Ericson2314 May 26 '12 at 17:29
  • Well, taking a look at your new revision, I see that you've tested it. Perhaps parameters are less like local variables than I thought. I edited your gist: https://gist.github.com/2794709 would that act any different? – Ericson2314 May 26 '12 at 17:31
  • Your new example behaves the same way. – okonomichiyaki May 28 '12 at 21:19
  • I think you're still a bit confused about this because #3 is not quite right: "the function effectively returns (lambda () (list 'local-x y)))" This is not what's actually returned. The return value is a function with two *free variables*. #4 is correct, `funcall` is ignorant of how it got there, but this isn't relevant. – okonomichiyaki May 28 '12 at 21:23
  • I would recommend downloading Emacs and Common Lisp and experimenting to convince yourself, they are both widely available. – okonomichiyaki May 28 '12 at 21:25