Can a dynamic function created in a method capture the class's own private property, and does it share it?

← Advanced Type System · Ref: Q2130

Yes - name the property in the CAPTURE LIST: '(counter: hits) is Notify as function'. The capture list is evaluated in the method that creates the dynamic function, where the class's private properties are visible, so it may name them (a dynamic class or function nested inside a dynamic function may likewise capture that function's own capture). A capture SHARES the object - EK9 has no primitives - so 'counter++' inside the function moves 'hits' itself: the class's own method chose to let the property escape, exactly as a method returning the property would. The BODY of a dynamic function or class cannot name 'hits' directly: a dynamic construct is not inside the object, so that is E06180 and the message names the capture. CHOOSE SHARED (this example) when the dynamic construct is meant to update the object - a callback recording into the object's tally, a listener appending to its log. CHOOSE A COPY, '(count: Integer(hits))', when the dynamic construct only needs a starting value it may then change without moving the property (Q2131). Rebinding the capture with ':=' inside the body never moves the property; only a mutating operator ('++', '+=', ':=:', ':~:', ...) acts on the shared object. A capture holds the OBJECT, not the field: 'hits := Integer(0)' in a method re-points the field and the function keeps the old object, so after 'reset()' a click no longer reaches 'hits' - only a mutating operator is shared.

Example

defines module qa.advancedtypes.capturesharesproperty

  defines function

    Notify() as abstract
      -> event as String

  defines class

    Tracker
      hits as Integer: 0

      //SHARED BY DESIGN: the callback updates the tracker's own tally.
      callback()
        <- rtn as Notify: (counter: hits) is Notify as function
          if event == "click"
            counter++

      //Re-points the field: the callback still holds the OLD object.
      reset()
        hits := Integer(0)

      hitCount()
        <- rtn as Integer: Integer(hits)

      default operator ?

  defines program

    CaptureSharesProperty()
      stdout <- Stdout()
      tracker <- Tracker()
      onEvent <- tracker.callback()
      stdout.println(`hits before: ${tracker.hitCount()}`)
      for event in ["click", "scroll", "click"]
        stdout.println(event)
        onEvent(event)
      stdout.println(`clicks counted: ${tracker.hitCount()}`)
      tracker.reset()
      onEvent("click")
      stdout.println(`after reset and a click: ${tracker.hitCount()}`)

Common mistakes

E06180 — The body of a dynamic function cannot name a property of the object around it - the dynamic function is not inside that object. Capture it in the capture list, '(counter: hits)', which shares the object so 'counter++' moves 'hits'; or capture a copy, '(counter: Integer(hits))', if the property must not move. See ek9 -h E06180 for details.

Incorrect:

() is Notify as function
          if event == "click"
            hits++

Correct:

(counter: hits) is Notify as function
          if event == "click"
            counter++
Other ways to ask this
  • How does a callback update a class's own field in EK9?
  • Why is reading a property inside a dynamic function body E06180?
  • Is '(counter: hits)' a copy or the same object?
  • How do I let a closure created in a method change the object's tally?

Coming from another language?

Java: a lambda in an instance method reads 'this.hits' implicitly but cannot assign it through a local copy, and an Integer field is immutable, so a shared counter needs an AtomicInteger or an int[] holder. Kotlin: a lambda captures 'this' and may assign the property directly. JavaScript: an arrow function captures 'this'. EK9 makes the escape explicit: the property is named in the capture list, so a reader sees at the creation site exactly which of the object's fields the dynamic construct can reach - and Integer is mutable in place, so '++' on the capture is visible through the property.

Keywords: private property, shared, capture, callback, capture list, dynamic function, closure, E06180