How does a dynamic function take a class property as a starting value without changing the property?

← Advanced Type System · Ref: Q2131

Capture a COPY made with the type's copy constructor: '(left: Integer(limit))'. The capture list is evaluated in the method that creates the dynamic function, where the class's private properties are visible, and 'Integer(limit)' makes a new Integer there - the dynamic function owns it, so 'left--' moves only its own working value and 'limit' stays where it was. The copy is made once, when the dynamic function is created; a second call of the method starts a fresh one from the property as it is then. Capturing the property itself, '(left: limit)', would SHARE the object and every 'left--' would move 'limit' too (Q2130). CHOOSE A COPY (this example) when the dynamic construct needs an initial value it then works on - a countdown, a cursor, a retry budget. CHOOSE SHARED when the dynamic construct exists to update the object - a callback recording into the object's tally. A built-in copies at its own level (a List copy is a new container holding the same elements); for your own type, its copy constructor or 'operator :=:' decides how deep the copy goes.

Example

defines module qa.advancedtypes.captureinitialvalue

  defines function

    Remaining() as abstract
      <- rtn as Integer?

  defines class

    Retries
      limit as Integer: 3

      //AN INITIAL VALUE: the countdown starts from the limit and owns its own copy.
      countdown()
        <- rtn as Remaining: (left: Integer(limit)) is Remaining as function
          left--
          rtn: Integer(left)

      currentLimit()
        <- rtn as Integer: Integer(limit)

      default operator ?

  defines program

    CaptureInitialValue()
      stdout <- Stdout()
      retries <- Retries()
      remaining <- retries.countdown()
      stdout.println(`limit before: ${retries.currentLimit()}`)
      stdout.println(`remaining: ${remaining()}`)
      stdout.println(`remaining: ${remaining()}`)
      stdout.println(`remaining: ${remaining()}`)
      stdout.println(`limit after: ${retries.currentLimit()}`)
      fresh <- retries.countdown()
      stdout.println(`fresh countdown: ${fresh()}`)
Other ways to ask this
  • How do I capture a copy of a property into a dynamic function in EK9?
  • Why does my closure change the class's field, and how do I stop it?
  • What is '(left: Integer(limit))' in a capture list?
  • How do I give a closure its own working value from a field?

Coming from another language?

Java: a lambda reads 'this.limit' live, so a snapshot is a local 'int start = limit;' taken before the lambda and copied into an int[] or AtomicInteger to be changed. Kotlin/JavaScript: copy into a local 'var' before the closure. EK9: the copy is written in the capture list itself, so the creation site states which fields are shared and which are starting values.

Keywords: copy constructor, private property, snapshot, initial value, capture, capture list, dynamic function, closure