;; 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)