September 02, 2009
Walter Bright wrote:
> S. wrote:
> I find it strange that people are continuing to reinvent nested functions in ugly ways.

The blocks are not nested functions, they are more like closures. There are some block copy functions that move a block to the heap (including the captured variables).

Nested functions can usually not be called after the defining function have returned. You cannot return blocks directly (since they are located on the stack), but you can return a heap copy of the block.
September 02, 2009
Martin Franhaufer, el  2 de septiembre a las 03:49 me escribiste:
> Walter Bright Wrote:
> 
> > Nick Sabalausky wrote:
> > > "Walter Bright" <newshound1@digitalmars.com> wrote in message news:h7l32i$ev$1@digitalmars.com...
> > >> S. wrote:
> > >>> I was wondering what other people thought about this addition to C++ by Apple.   Heh.
> > >>>
> > >>> http://arstechnica.com/apple/reviews/2009/08/mac-os-x-10-6.ars/10
> > >>
> > >> I find it strange that people are continuing to reinvent nested functions in ugly ways.
> > > 
> > > No offense, but Ruby and Python users would probably get a good chuckle at hearing the D creator saying that ;)
> > 
> > Perhaps they would, but I think they would agree that a nested function should follow the same syntax as a regular function.
> 
> Yes - Pascal has been doing that for years :-)

GCC support them as an extension to C for ages too.

-- 
Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/
----------------------------------------------------------------------------
GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145  104C 949E BFB6 5F5A 8D05)
----------------------------------------------------------------------------
A veces quisiera ser un barco,
para flotar como floto siendo humano,
y no hundirme como me hundo
September 02, 2009
Leandro Lucarella wrote:

> GCC support them as an extension to C for ages too.
> 

True for nested functions but nested functions are not blocks.

typedef void (*foo_t)(void);

foo_t getSomeFunc()
{
	int x = 4;
	void bar(void) {
	    printf("%d\n", x);
	}
	return bar;
}

foo_t func = getSomeFunc();
func(); // Undefined, nested functions cannot be returned and in any case, no variable capturing will happen, so the variable x is undefined when func is called above getSomeFunc.


On the contrary, the following would work very well with blocks:

typedef void (^foo_t)(void);

foo_t getSomeBlock()
{
	int x = 4;
	void ^bar(void) {
	    printf("%d\n", x);
	}
	return Block_copy(bar);
}

foo_t blk = getSomeBlock();
blk(); // Will happily print 4
Block_release(blk); // free up memory of block


Though, they could probably make some hacks to gcc to add the copying and variable capturing to nested functions. Such hacks would most likely break a lot of code as the ABIs would have to be changed.

This means that blocks are very suitable for enqueuing in something using the thread pool pattern, this is what GCD does and in my humble opinion GCD is a very very nice concurrency model for C. The main issues now being that GCD is seriously in need of standardisation through POSIX, and the C language.

I suppose that blocks will not be added to C officially for quite some time, and therefore neither in POSIX. I think they should work out a version of C that includes that in the standard, like they have done for embedded applications, i.e. a set of extensions to the core language.


/ Matt
September 02, 2009
Mattias Holm wrote:
> Walter Bright wrote:
>> S. wrote:
>> I find it strange that people are continuing to reinvent nested functions in ugly ways.
> 
> The blocks are not nested functions, they are more like closures.

Nested functions do closures in a straightforward way, so by leaving off nested functions they were forced to make an ugly syntax <g>. This is why I shake my head in just not understanding the process that led to their design.


> There are some block copy functions that move a block to the heap (including the captured variables).
> 
> Nested functions can usually not be called after the defining function have returned. You cannot return blocks directly (since they are located on the stack), but you can return a heap copy of the block.

This is handled in D automatically.
September 02, 2009
Walter Bright wrote:
> Tim M wrote:
>> Walter: may I ask with this, reddit posts and dobb's code post, why
>> the interest in this particular topic right now? Didn't you implement
>> this a long time ago?
> 
> It was one of the first things implemented in D.
> 
> But I was thinking about it lately as I prepare the materials for the Compiler Construction seminar in a few weeks. Everyone tells me they are simple and obvious, yet in language after language they get added in bizarre ways that suggest that *somebody*, me or them, is just not getting it.
> 
> So I thought it was time for an article.
> 
> I mean, how can one miss the most stunningly obvious syntax:
> 
>    int foo(int a)
>    {
>          int bar(int i) { return a + 3; }
> 
>          return bar(3);
>    }
> 
> and that nested functions are useful for many more things than passing around pointers to them. For example, as helper functions.

It has been a GCC extension for a long time:

http://gcc.gnu.org/onlinedocs/gcc-2.95.3/gcc_4.html#SEC65
September 02, 2009
Mattias Holm, el  2 de septiembre a las 18:08 me escribiste:
> Leandro Lucarella wrote:
> 
> >GCC support them as an extension to C for ages too.
> 
> True for nested functions but nested functions are not blocks.

I know, you removed the context of my response, I was refering to nested functions...

-- 
Leandro Lucarella (luca) | Blog colectivo: http://www.mazziblog.com.ar/blog/
----------------------------------------------------------------------------
GPG Key: 5F5A8D05 (F8CD F9A7 BF00 5431 4145  104C 949E BFB6 5F5A 8D05)
----------------------------------------------------------------------------
Hoy estuvimos en el museo de antropología, pero yo voy a volver
para estar por lo menos un día ahí adentro... es una locura, como Disney pero
de indigenas
	-- Carla Lucarella (10/2008 contando de su viaje a México)
September 02, 2009
grauzone wrote:
> It has been a GCC extension for a long time:
> 
> http://gcc.gnu.org/onlinedocs/gcc-2.95.3/gcc_4.html#SEC65

Which further makes it a mystery why this has to be badly reinvented.

(But note that the gcc extension has the same problem D1 has with them; the closures don't survive the end of the function. This was fixed with D2.)
September 03, 2009
Walter Bright wrote:
> Nested functions do closures in a straightforward way, so by leaving off nested functions they were forced to make an ugly syntax <g>. This is why I shake my head in just not understanding the process that led to their design.

That is because nested functions and block closures have different purposes.

Block closures avoid delocalization of code. For example, in Smalltalk, you could write something like the following to calculate the sum of the first 100 integers:

	s := 0.
	1 to: 100 do: [ :i | s := s + i ].


Packaging the 's := s + i' block inside a nested function instead would (aside from unnecessarily having to also pass 's' by reference or by value-result to the function) require you to move back and forth between the piece of code above and the nested function in order to comprehend it. More generally, it allows you to write custom control structures, such as map, fold, and filter to generate more compact an coherent code. This is especially prevalent in functional languages (Scheme, LISP, Haskell, the whole ML family) and Smalltalk or Ruby.

Research seems to indicate [1] that there is indeed a measurable readability benefit to this style of programming.

The benefit of nested functions is something different. In essence, a nested function allows you to share state with the parent function (absent shared state, there is little reason to not make the nested function a top-level function instead). However, nested functions are hidden inside their parent, which means that they are not reusable; on top of that, object-oriented languages already have means to share state between multiple methods (which is sometimes imperfect, but so are nested functions).

So, there are perfectly good reasons to have block closures, but not nested functions. Obviously, there are trade-offs, which may not appeal to everybody, but the design is still a rational one.


			Reimer Behrends


[1] http://infoscience.epfl.ch/record/138586
September 03, 2009
Reimer Behrends wrote:
> The benefit of nested functions is something different. In essence, a nested function allows you to share state with the parent function (absent shared state, there is little reason to not make the nested function a top-level function instead).


The reason for nested functions are:

1. factoring out repeated code within the function into a nested function

2. locality - a nested function is adjacent to where it is used, rather than somewhere else

3. isolation - by scope rules, it is easy to see the nested function and all calls to it

These may be mundane, but they make for the code being more readable than the clumsy workarounds necessary without them. They can also often be inlined by the compiler, making them a no-cost abstraction.

> However, nested functions are hidden inside their parent, which means that they are not reusable;

That's actually a feature, see (3).

> on top of that, object-oriented languages already have means to share state between multiple methods (which is sometimes imperfect, but so are nested functions).

Nested classes (Java) and functors (C++) are pretty ugly next to nested functions.

One could argue that gcc has them as an extension but nobody uses them. My experience with adding extensions to DM C++ is that nobody uses them because it is non-standard, not because they are a bad idea.

> So, there are perfectly good reasons to have block closures, but not nested functions. Obviously, there are trade-offs, which may not appeal to everybody, but the design is still a rational one.
> 
> 
>             Reimer Behrends
> 
> 
> [1] http://infoscience.epfl.ch/record/138586
September 03, 2009
Walter Bright wrote:
> The reason for nested functions are:
> 
> 1. factoring out repeated code within the function into a nested function
> 
> 2. locality - a nested function is adjacent to where it is used, rather than somewhere else
> 
> 3. isolation - by scope rules, it is easy to see the nested function and all calls to it

You don't actually need nested functions for that. Consider, for example, Tarjan's algorithm [1] to find strongly connected components in a directed graph. It's normally a prime example of where you would use nested functions: It's got some scaffolding to set up temporary state, then repeatedly calls a recursive helper algorithm that operates on the temporary state. Without going into the details of the algorithm, you can basically write it as follows (using Scala as an example, though of course it would also work in D, OCaml, etc.):

class DirectedGraph[V] {
  ...
  def stronglyConnectedComponents: Set[Set[V]] = {
    def recursiveHelper(vertex: V) = { ... }
    ...
  }
  ...
}

However, you could also factor out the algorithm into a class of it's own:

class StronglyConnectedComponents[V](val graph: DirectedGraph[V]) {
  def findAll: Set[Set[V]] = { ... }
  private def recursiveHelper(vertex: V) = { ... }
}

This satisfies all your three criteria above. The potentially repeated code is factored out into its own function, the helper function is next to where it is used, and you can see who call it since it is private.

However, unlike a nested function, you have additional benefits: You can reuse the helper function, for starters. And since the algorithm isn't an integral part of the DirectedGraph class anymore, you can furthermore improve the refactoring, since the algorithm doesn't need the full graph, but only a set of vertices and a successor function:

class StronglyConnectedComponents[V](val vertices: Set[V],
    val successors: V => Set[V]) {
  def findAll: Set[Set[V]] = { ... }
  private def recursiveHelper(vertex: V) = { ... }
}

Now we can use the algorithm not only for directed graphs, but also for other, similar data structures that do not need to be a subtype of DirectedGraph.


>> However, nested functions are hidden inside their parent, which means that they are not reusable;
> 
> That's actually a feature, see (3).

As I showed above, you can have both locality and reusability. These are not mutually exclusive properties.

>> on top of that, object-oriented languages already have means to share state between multiple methods (which is sometimes imperfect, but so are nested functions).
> 
> Nested classes (Java) and functors (C++) are pretty ugly next to nested functions.

I am not even talking about nested classes, just classes (see above). Classes are the standard vehicle in most object-oriented languages for having multiple functions operating on shared state.

Mind you, I'm not saying that this is the only approach to deal with such issues (and it has imperfections of its own). Just that it is an alternative, equally valid way of doing things.

			Reimer Behrends


[1] http://en.wikipedia.org/wiki/Tarjan%27s_strongly_connected_components_algorithm