FIX.org, NEXT.md, DISCUSS.org, docs/DISCUSS.md and the session handoff at the root are one TODO.org now: 293 entries under seven subsystem headings, each carrying an org keyword that says where it stands. A DONE entry is a few lines saying what was decided and what that rules out; the reasoning that would not compress — the embedding spike and the four reports the hand-written x86 backend was built from — moved into docs/BUILT.md instead, and its entries point there in one line. Every entry was checked against the tree before it got a keyword, and the prose was wrong in both directions. Things the deleted files called open were built: the first-evaluation stall, main being redefinable, macro parameter lists, the type-limit constants, the array constructors, the byte fills, inc/dec, the discard's fontification, the Emacs buffers, rt_die's _exit, the backtrace surface, and the acceptance failure that could print and still exit zero. Things they called done were not: the backend reports' no-plan buckets had gone stale in the other direction, the value-dependent defvar was superseded rather than built, and macro-expansion source locations are on an unmerged lane, so that entry is NEXT and names the branch. Every comment that cited one of the five by name now cites a heading that exists, in TODO.org or in docs/BUILT.md. The session reports under docs/handoffs/ keep naming the files they worked on, because rewriting them would falsify what those sessions did; each carries a note saying where the content went.
68 lines
3.2 KiB
Plaintext
68 lines
3.2 KiB
Plaintext
;; The narrow function type. A (CFn [T ...] R) is the bare code address —
|
|
;; one word, no environment, and therefore nothing that can capture. A
|
|
;; (Fn [T ...] R) is that address and the environment beside it, two words.
|
|
;;
|
|
;; The reason there are two rather than one: an environment on every signature
|
|
;; would tax every function in every program for a feature most of them never
|
|
;; use. With CFn written where it is wanted, an ordinary defn emits exactly
|
|
;; the signature it emitted before capture existed, and a call to it by name
|
|
;; is byte-for-byte what it was.
|
|
;;
|
|
;; The C is information and not decoration. A value with no environment is the
|
|
;; only kind that could ever cross to C, and under the --no-conditions
|
|
;; direction TODO.org, "CFn and C's calling convention", records — where a
|
|
;; signature that cannot transfer drops the channel too — one becomes literally
|
|
;; a C function pointer. It is not that today: a declare cannot take a function
|
|
;; type at all, and the refusal it meets says so. The name points at what the
|
|
;; type is, and at where it is going.
|
|
;;
|
|
;; **Nobody needs CFn.** An Fn accepts everything a CFn does, so the narrow
|
|
;; one is reached for on purpose, for one of four reasons: handing a function
|
|
;; to C, later; a table of bare addresses; forbidding capture at a boundary;
|
|
;; and the one that is likeliest in practice — a *named* function handed to an
|
|
;; Fn parameter goes through the widening thunk and pays an indirect hop per
|
|
;; call, where a CFn parameter is a direct call. (map-in-place s double) is
|
|
;; the example, and [apply-bare] below is it in miniature.
|
|
;;
|
|
;; Coercion is one-way. A defn's address and a non-capturing literal satisfy
|
|
;; both. An Fn does not narrow to a CFn — there is nowhere for the
|
|
;; environment to go — and fn-cfn-narrow.flan is that refusal.
|
|
|
|
(defn double [x i32] i32 (* x 2))
|
|
(defn negate [x i32] i32 (- 0 x))
|
|
|
|
;; Taking the narrow one. Nothing that reaches here can carry an environment,
|
|
;; which is what the signature is saying.
|
|
(defn apply-bare [f (CFn [i32] i32) x i32] i32 (f x))
|
|
|
|
;; And the wide one, which is what almost every higher-order signature wants.
|
|
(defn apply-any [f (Fn [i32] i32) x i32] i32 (f x))
|
|
|
|
;; A CFn returned. It is a link-time constant with nothing behind it, so
|
|
;; handing one back is no different from handing it down — which is exactly
|
|
;; what a capturing value cannot do.
|
|
(defn pick [up bool] (CFn [i32] i32) (if up double negate))
|
|
|
|
;; A CFn parameter widened to an Fn at a call: the address goes where an
|
|
;; environment would be and the thunk reads it back out. This is the hop the
|
|
;; narrow type exists to avoid.
|
|
(defn through [f (CFn [i32] i32) x i32] i32 (apply-any f x))
|
|
|
|
(defn main [] i32
|
|
;; A name into a CFn, and into an Fn.
|
|
(println (apply-bare double 4))
|
|
(println (apply-any negate 4))
|
|
;; A literal that captures nothing into a CFn.
|
|
(println (apply-bare (fn [x] (+ x 1)) 4))
|
|
;; And one that does capture, into an Fn.
|
|
(let [k 10]
|
|
(println (apply-any (fn [x] (+ x k)) 4)))
|
|
;; A returned CFn, called through a computed head.
|
|
(println ((pick true) 21))
|
|
(println ((pick false) 21))
|
|
;; The widening, twice over: a CFn local through a CFn parameter into
|
|
;; an Fn parameter.
|
|
(let [g double]
|
|
(println (through g 5)))
|
|
0)
|