On Mon, Apr 30, 2012 at 4:01 AM, Stéphane Ducasse <stephane.ducasse@inria.fr
wrote:
foo
|b| b := 3. ^ [ |a | a :=2. a]
In my stupid lisp implementation an environment is represented by
bindings and closure points to their own environment
so [ |a | a :=2. a] points to (b.3) (toplevel) (and not top-level)
and for closure execution we just added temporaly a binding for a to the closure environment. eval a:=2. a in (a) (b.3) (toplevel)
This is the wrong example.
I understand your example below and the fact that closure environment is allocated on the heap via an array to be independent of the activation context but I do not get why consing the local environment to be one current one is wrong.
I mean the example is not a useful example because it doesn't show the problem "my" representation solves, which is to make closures independent of their enclosing activations if they outlive their dynamic extent.
(define (%apply proc largs) (cond ((%primitive? proc) (%apply-internal proc largs)) ((%closure? proc) (%apply-closure proc largs)) (else (error "Bad ! Un-apply-able object !" proc))))
(define (%apply-closure proc largs) ;; apply a closure: evaluate proc body in ;; extended the closure environment with ;; proc arguments and largs (%eval (%closure-body proc) (%extend-env (%closure-args proc) largs (%closure-env proc))))
(define (%eval-lambda expr env) ;; lambda in Scheme captures the environment at compile time ;; expr = ((x) (+ x 2)) (%make-closure (car expr) (cadr expr) env))
You need to consider how to implement this example:
Object subclass: #AbstractSuperclass
AbstractSuperclass>>computeV | v | v := 0. self maybeCapture: [v := v + 1. v]. ^self maybeBlock: v
AbstractSuperclass subclass: #CaptureBlock instanceVariableNames: 'block'
CaptureBlock>>maybeCapture: aBlock block := aBlock
CaptureBlock>>maybeBlock: value ^block
AbstractSubclass subclass: #DontCaptureBlock
DontCaptureBlock>>maybeCapture: aBlock
DontCaptureBlock>>maybeBlock: value ^value
Then | thingOne thingTwo | thingOne := CaptureBlock computeV. thingTwo := DontCaptureBlock computeV. (1 to: 10) collect: [:ignore| { thingOne value. thingTwo value }]
should answer #((1 0) (2 0) (3 0) ... (10 0))
if 0 value returns 0? yes. (it confused me for a while).
right? So where does the l-value for v in computeV live? Iy must be an l-value since it is assigned to in the block in computeV. It must outlive the activation of computeV since CaptureBlock>maybeCapture: captures the block and CaptureBlock>>maybeBlock: answers it and the doit evaluates it. If it lives on the stack of the activation of computeV then there is no problem in a Context VM, but in a VM that maps activations to stack frames something special (and slow) has to happen when returning from computeV. However, if the l-value lives in a separate Array (as it does in my VisualWorks and Squeak closure implementations, and as happens in some Lisp implementations), nothing special has to happen. The block refers to the Array, *not* to the activation of computeV.
Make sense now?
Yes you allocate on the heap closure binding to be orthogonal to activation context.
I will reread your blog now.
Stef
-- best, Eliot