clarity-lang / reference

The Clarity Reference
149 stars 34 forks source link

Local functions #56

Open kantai opened 1 year ago

kantai commented 1 year ago

Iteration methods (especially map and filter) could be much simpler with the introduction of local functions. A local function can read the surrounding function's context, but defines its own arguments and variables. Its variable may shadow the surrounding function's variables.

An example from pox-2:

(define-private (add-pox-addr-to-ith-reward-cycle (cycle-index uint) (params (tuple
                                                            (pox-addr (tuple (version (buff 1)) (hashbytes (buff 32))))
                                                            (reward-set-indexes (list 12 uint))
                                                            (first-reward-cycle uint)
                                                            (num-cycles uint)
                                                            (stacker (optional principal))
                                                            (amount-ustx uint)
                                                            (i uint))))
    (let ((reward-cycle (+ (get first-reward-cycle params) (get i params)))
          (num-cycles (get num-cycles params))
          (i (get i params))
          (reward-set-index (if (< i num-cycles)
            (let ((total-ustx (get-total-ustx-stacked reward-cycle))
                  (reward-index
                      ;; record how many uSTX this pox-addr will stack for in the given reward cycle
                      (append-reward-cycle-pox-addr
                        (get pox-addr params)
                        reward-cycle
                        (get amount-ustx params)
                        (get stacker params)
                        )))
                  ;; update running total
                  (map-set reward-cycle-total-stacked
                     { reward-cycle: reward-cycle }
                     { total-ustx: (+ (get amount-ustx params) total-ustx) })
                  (some reward-index))
            none))
          (next-i (if (< i num-cycles) (+ i u1) i)))
    {
        pox-addr: (get pox-addr params),
        first-reward-cycle: (get first-reward-cycle params),
        num-cycles: num-cycles,
        amount-ustx: (get amount-ustx params),
        stacker: (get stacker params),
        reward-set-indexes: (match
            reward-set-index new (unwrap-panic (as-max-len? (append (get reward-set-indexes params) new) u12))
            (get reward-set-indexes params)),
        i: next-i
    }))

(define-private (add-pox-addr-to-reward-cycles (pox-addr (tuple (version (buff 1)) (hashbytes (buff 32))))
                                               (first-reward-cycle uint)
                                               (num-cycles uint)
                                               (amount-ustx uint)
                                               (stacker principal))
  (let ((cycle-indexes (list u0 u1 u2 u3 u4 u5 u6 u7 u8 u9 u10 u11))
        (results (fold add-pox-addr-to-ith-reward-cycle cycle-indexes
                         { pox-addr: pox-addr, first-reward-cycle: first-reward-cycle, num-cycles: num-cycles,
                           reward-set-indexes: (list), amount-ustx: amount-ustx, i: u0, stacker: (some stacker) }))
...

Combining local functions and a range function (https://github.com/clarity-lang/reference/issues/50):

(define-private (add-pox-addr-to-reward-cycles (pox-addr (tuple (version (buff 1)) (hashbytes (buff 32))))
                                               (first-reward-cycle uint)
                                               (num-cycles uint)
                                               (amount-ustx uint)
                                               (stacker principal))
  (let ((local-add-to-reward-cycle 
            (lambda (cycle-index uint)
               (let ((reward-cycle (+ first-reward-cycle cycle-index))
                     (reward-set-index 
                       (if (< cycle-index num-cycles)
                         (let ((total-ustx (get-total-ustx-stacked reward-cycle))
                               (reward-index
                                ;; record how many uSTX this pox-addr will stack for in the given reward cycle
                                (append-reward-cycle-pox-addr pox-addr reward-cycle amount-ustx stacker)))
                           ;; update running total
                           (map-set reward-cycle-total-stacked
                                    { reward-cycle: reward-cycle }
                                    { total-ustx: (+ amount-ustx total-ustx) })
                           (some reward-index))
                         none))))))
        (results (map local-add-to-reward-cycle (range u0 u11)))
...

Note how much of the fold accumulator is dedicated just to marshaling local variables to the iteration.

MarvinJanssen commented 1 year ago

+1

Lambdas would make many things a lot more efficient.