Discontiguous Lexical Scope for Multiply Dispatched Functions

blabatt
Thu, 30 May 2024 15:19:42 +0000
General Usage

I would like to create a closure that binds free variables with respect to a discontiguous “lexical environment” that encapsulates both the disparate method implementations and collective reference to a multiply-dispatched function. Concretely, I’d like to do something like the following:

function f(x::Integer)
  x + a
end

function f(x::Float64)
  x * a
end

function g()
  a = 5
  f
end

When invoked (eg, via g()(3.0)), this fails with the error:

ERROR: UndefVarError: a not defined

On the other hand, if I define all methods of the closure locally within a single encompassing code block, the technique works:

function h()
  a = 5
  f(x::Integer) = x + a
  f(x::Float64) = x * a
  f
end

Then h()(3) yields 8 and h()(3.0) yields 15.0, as expected.

The first example would work if I were to assign a value to the function’s free variable (a) in the same scope as the definition of the function:

a = 5

function f(x::Integer)
  x + a
end

function f(x::Float64)
  x * a
end

function g()
  a = -9999
  f
end

Now, g()(3) yields 8 and g()(3.0) yields 15.0, as h does in the second example. The value of a assigned internally to g (ie, via a = -9999) is completely ignored, however. In this third example, I would have expected the later, “closer” assignment (of -9999 here) to have shadowed over the assignment in the top-level scope.

Why does Julia not bind free variables the same way in the first and second cases? Since it is discernible from an exclusively textual / lexical reading of the code what encompassing scope is intended by the first example, it seems to me that the approach would be consistent with the spirit of the language being lexically scoped. There is an organizational advantage to being able to extend methods in different sections of the code — ie, in different modules, etc — but I was unable to do this without losing the centralized binding of the free variable in the encompassing function g, as desired. Is there an alternate way to achieve the kind of closure envisioned without sacrificing the ability to move method definitions to ad-hoc locations elsewhere in the code?

10 posts - 4 participants