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

Reply via email to