; Iter 16b.6 — LetRec inside a polymorphic enclosing fn.
; `apply_n_times : Forall(a). Fn(Int, a, Fn(a) -> a) -> a`
; applies `f` to `x` exactly `n` times. The recursive helper `loop`
; captures `f` from the enclosing fn's params — `f`'s declared type
; is `Fn(a) -> a`, which mentions the outer fn's type var `a`. The
; 16b.6 lift produces a synthetic top-level fn
;   `loop$lr_0 : Forall(a). Fn(Int, a, Fn(a) -> a) -> a`
; and rewrites `(app loop k acc)` → `(app loop$lr_0 k acc f)`.
;
; `main` drives `apply_n_times` at TWO distinct type instantiations
; so codegen's monomorphisation queue picks up `loop$lr_0` twice
; (once at `a = Int` to compute `succ` applied 5x to 0 → 5; once at
; `a = Bool` to compute `not` applied 4x to false → false). This
; exercises the substitution of the outer Forall.vars into both the
; original LetRec params and the appended capture types.
;
; Expected stdout (one per line): 5, false.

(module poly_rec_capture

  (fn succ
    (doc "Helper: increment Int. Used as the Fn(Int) -> Int instance.")
    (type (fn-type (params (con Int)) (ret (con Int))))
    (params x)
    (body (app + x 1)))

  (fn flip
    (doc "Helper: boolean negation as a top-level fn (so it has an adapter for value-position).")
    (type (fn-type (params (con Bool)) (ret (con Bool))))
    (params b)
    (body (app not b)))

  (fn apply_n_times
    (doc "Apply f to x exactly n times. Polymorphic in a.")
    (type
      (forall (vars a)
        (fn-type
          (params (con Int) a (fn-type (params a) (ret a)))
          (ret a))))
    (params n x f)
    (body
      (let-rec loop
        (params k acc)
        (type (fn-type (params (con Int) a) (ret a)))
        (body
          (if (app == k 0)
            acc
            (app loop (app - k 1) (app f acc))))
        (in (app loop n x)))))

  (fn main
    (doc "Drive apply_n_times at Int (succ 5 times from 0) and Bool (not 4 times from false).")
    (type (fn-type (params) (ret (con Unit)) (effects IO)))
    (params)
    (body
      (seq (do io/print_int (app apply_n_times 5 0 succ))
           (do io/print_bool (app apply_n_times 4 false flip))))))
