Leaking II
Kartik Agaram pointed out that I haven’t mentioned what happens during the assignment of pre-declared variables in scope.
Here are a few scenarios. This first example signals an error, because a has no binding when print a is called:
def foo (b):
a ← b
foo 15
print a
This example prints 15:
a ← 5
def foo (b):
a ← b
foo 15
print a
Which seems acceptable —- but this also prints 15: def foo (b): a ← b
a ← 5
foo 15
print a
This is a behavior that, while not inconsistent or incomprehensible, does prevent the encapsulation of arbitrary code in a scope. I have decided that I want to hold to this maxim:
Any arbitrary call made inside an otherwise empty procedure should have no side effects lasting beyond the end of the wrapping procedure.
Which is to say:
def foo ():
(some-function)
(foo)
must not change the global scope. This isn’t the case for any lisp that I know of —- it would cause significant problems:
(define (inc-er)
(let (a 0)
(lambda () (set! a (+ a 1)))))
Tricks like this counter would no longer work —- the set! called (in an otherwise empty lambda) could have no effect on the value of a. (And even if you allow it to affect its own personal a, there are many reasons to have multiple functions all using the same closed values. It’s a common lisp pattern.) Lisp survives by having both let (no side effects) and set (side effects as discussed above).
I want to eliminate let from my language’s vocabulary —- it causes unnecessary proliferation of indentation and the creation of scopes without semantic meaning. The let in inc-er causes a scope to be created, by way of function call, but that function has no meaningful purpose except to mark the time during which a exists. Procedures should be created based on what they do and what they return, not merely to piggy-back on their scope.
So, there must be some way to declare that you intend to let some side effects escape from a procedure. My current intention is to use leak to do so:
def inc-er ():
a ← 0
λ():
leak a
a ← a + 1
That is: assignment has NO effect outside the current scope unless the particular symbol being assigned has previously been leaked from that scope.
This is NOT a delightful solution —- it is longer and less pleasant than the scheme version, and leak would rapidly become repetitive to write; repetition is what we have computers for. There is no clear way to abstract leak using f-exprs; currently Oyster doesn’t use true macros. So I am unsure if this is a final answer, but it does satisfy my maxim while still entertaining the closure-magic that makes scheme so pleasurable.
