Skip to content

Mutual recursion of adjacent function statements #5339

Open
@rakudrama

Description

@rakudrama

Dart does not support mutual recursion of functions declared in blocks (aka function-statements)

In this respect Dart is inferior to almost every other language that has a decent functional subset (including Python and JavaScript).

One solution would be for adjacent function-statements come into scope together to allow mutually recursion.

I am opening this feature request as Issue #315 could be narrowly construed to be concerned with the mismatch between the language and the specification.

The work-around are all painful:

W1. Use global functions - this pollutes the top level namespace, requires the programmer to manually lambda-lift the closed-over values and to manually box closed-over mutated variables. The code has to be moved out of the current function/method body to somewhere else in the file.

W2. Use a class with mutually recursive methods. This also pollutes the top level namespace and requires manual lambda-lifting or the extra hand-written code of a constructor.

W3. Use local variables and assign them with closures. This has the problem that the variables are not guarantee to not change. The reader of the code would be justifiably nervous that some tricky control flow might be implemented by a second assignment to one of the variables. This version also pollutes the global namespace with typedefs if the local variables are to be declared with a function type.

When you are writing a program you don't necessarily anticipate mutual recursion. When the need arises you have to refactor your program. It is really the job of a compiler to do the leg-work involved in these work-arounds, and a compiler is better situated to choose the implementation strategy depending on the characteristics of the mutually recursive functions.

The proposal is that adjacent function-statements come into scope together to allow mutually recursion.
The grouping of the function-statements can be expressed in the grammar.
Breaking the mutual recursion at labels or other statements is a simple way to ensure that there is no possibility of intermediate half-initialized state visible to the programmer while giving the compiler maximum latitude for implementation.

What follows is an example of a function that I believe should work in Dart and I would not need to rewrite in some awkward way if I was using Python or JavaScript.

/** Format nested lists, with strings in quotes. */
format(object, [prefix = '\n', suffix = '\n', open = '[', close = ']', comma = ', ']) {
  var fragments = [];
  void emit(e) { fragments.add(e); }

  void walkObject(o) {
    if (o == null) {
      emit('null');
    } else if (o is String) {
      emit("'$o'");
    } else if (o is List) {
      walkList(o);
    } else {
      emit('$o');
    }
  }
  void walkList(List xs) {
    emit(open);
    var sep = '';
    for (var x in xs) {
      emit(sep);
      sep = comma;
      walkObject(x);
    }
    emit(close);
  }

  emit(prefix);
  walkObject(object);
  emit(suffix);
  return Strings.concatAll(fragments);
}

Metadata

Metadata

Assignees

Labels

area-languageDart language related items (some items might be better tracked at github.com/dart-lang/language).type-enhancementA request for a change that isn't a bug

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions