Skip to content

Deferred variable type propagation

jckarter edited this page Nov 24, 2010 · 4 revisions

Motivation

Initializing a variable that must be initialized in an inner scope but available to an outer scope is currently awkward, especially if the type is complex and needs to be derived. For example, to invoke a callable wrapped in a try block and forward its return values, you currently have to do this:

  // From sqlite.clay
  withTransaction(db: SqliteDB, fn) {
      runStatement(db, "begin");

      var returnValues = Type(captureValues(...fn()))();
      try {
          returnValues = captureValues(...fn());
      } catch (ex) {
          runStatement(db, "rollback");
          throw ex;
      }

      runStatement(db, "commit");
      return ...forwardValues(returnValues);
  }

This would be less awkward if the variable could be declared without a type, and its type propagated by a subsequent initialization operation.

Proposed implementation

Allow variable declarations of the form var a;. The variable a cannot be used or assigned to before first being initialized with <--. The initialization may happen within a nested scope, as long as a is not referenced beforehand:

  // Invalid code
  var a;
  var b = a + 1; // NO
  a <-- 7;
  // Invalid code
  var a;
  var b = x => a + x; // NO (tries to capture a)
  a <-- 7;
  // Invalid code
  var a;
  a = 7; // NO (can't assign to a until it's been initialized and given a definite type)
  // Valid code
  var a;
  try {
      a <-- 7;
  } catch (ex) { ... }
  var b = a + 1;

The type of a is propagated by the first initialization applied to a. If a is initialized in a branch, it must be initialized to the same type in every other possible branch:

  var a;
  if (christmas?)
      a <-- 25;
  else
      a <-- 31;

With this feature, the awkward Type(...)() construct in the motivating example is no longer necessary:

  // From sqlite.clay
  withTransaction(db: SqliteDB, fn) {
      runStatement(db, "begin");

      var returnValues;
      try {
          returnValues <-- captureValues(...fn());
      } catch (ex) {
          runStatement(db, "rollback");
          throw ex;
      }

      runStatement(db, "commit");
      return ...forwardValues(returnValues);
  }

Discussion

This exposes the var a = 1; vs a = 1 vs a <-- 1 initialization/assignment dichotomy wart really bad.

Clone this wiki locally