Re: Apple Blocks added to C++?

2009-09-03 Thread Reimer Behrends

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


Re: Apple Blocks added to C++?

2009-09-03 Thread Reimer Behrends

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 . 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