The mechanical half, ahead of the parser change that needs it. tools/unit-return.py
fills the empty slot with () and rewrites Unit as () wherever a type is spelled --
(Fn [i32] Unit), (Map i32 Unit), a return type written out.
Deciding whether a defn already had a return type is the whole difficulty, and
the script does it the way parse.ml did: is_type_form is transcribed rather than
improved, because being identical to the parser it replaces is what makes the
sweep meaning-preserving. It is re-runnable, so the lanes that branched before
this can have the same pass at merge:
python3 tools/unit-return.py .
python3 tools/unit-return.py --in-strings test/test_flan.ml test/test_acceptance.ml \
test/test_session.ml emacs/test-flan-dev.el emacs/test-flan-mode.el
python3 tools/unit-return.py --raw-ml lib/prelude.ml
python3 tools/unit-return.py --in-html web/index.html
-v logs every defn it saw and what it decided, which is how a sweep of 440 sites
gets reviewed at all. Embedded modes pool a file's type declarations across all
its fragments, because a snippet split across concatenation -- decls ^ "(defn f
[s [u8]] Cursor ...)" -- cannot see the names the other half declared; pooled
names count only in bare-symbol position, for the same reason the prelude's do.
A fragment that cuts off mid-form is skipped rather than guessed at. Five sites
in test_flan.ml still needed a hand, and they are in this commit.
Two things ride along because the sweep needs them: parse.ml reads a lone () as
the return type of a function with no body, which was not a shape the old
optional slot could produce; and the map refusals name () rather than Unit, since
that is now the spelling a caller wrote.
89 lines
3.2 KiB
Plaintext
89 lines
3.2 KiB
Plaintext
;; Function values, the non-escaping kind: a code address and no environment
|
|
;; beside it. Capture does not exist, so nothing here can outlive anything.
|
|
;;
|
|
;; This is a Lisp-1 — one top-level namespace, enforced — so a bare function
|
|
;; name *is* the function and there is no #' to write.
|
|
|
|
(defn double [x i32] i32 (* x 2))
|
|
(defn negate [x i32] i32 (- 0 x))
|
|
(defn square [x i32] i32 (* x x))
|
|
|
|
;; The shape map/filter/reduce want: the function arrives as a parameter, is
|
|
;; called, and is never stored.
|
|
(defn each! [xs [i32] f (Fn [i32] i32)] ()
|
|
(dotimes [i (len xs)]
|
|
(set (at xs i) (f (at xs i)))))
|
|
|
|
(defn fold [xs [i32] f (Fn [i32] i32)] i32
|
|
(let [t 0]
|
|
(dotimes [i (len xs)]
|
|
(set t (+ t (f (at xs i)))))
|
|
t))
|
|
|
|
;; A comparator, which is the other half of what was blocked: a sort that is
|
|
;; told the order rather than having it written in. Insertion sort, because the
|
|
;; point here is the parameter and not the algorithm.
|
|
(defn sort-by! [xs [i32] before? (Fn [i32 i32] bool)] ()
|
|
(dotimes [i (len xs)]
|
|
(let [j i]
|
|
(while (and (> j 0) (before? (at xs j) (at xs (- j 1))))
|
|
(swap-i32! xs j (- j 1))
|
|
(set j (- j 1))))))
|
|
|
|
(defn ascending [a i32 b i32] bool (< a b))
|
|
(defn descending [a i32 b i32] bool (> a b))
|
|
|
|
;; Returning one. A function value is a link-time constant with no environment,
|
|
;; so handing it back up is no different from handing it down.
|
|
(defn pick [up bool] (Fn [i32 i32] bool)
|
|
(if up ascending descending))
|
|
|
|
;; A function value calling another, and the transfer channel crossing an
|
|
;; indirect call: a callee reached by pointer signals exactly as one reached by
|
|
;; name, and the handler is established across the call.
|
|
(defstruct TooBig [n i32])
|
|
|
|
(defn checked [x i32] i32
|
|
(when (> x 100) (signal (TooBig {.n x})))
|
|
x)
|
|
|
|
(defvar seen i32)
|
|
|
|
;; A handler-bind and an fn literal in *one* function, which is the case that
|
|
;; would catch the two lifted-function name sequences sharing a counter: both
|
|
;; are lifted out of [handles] and both are numbered within it.
|
|
(defn handles [] ()
|
|
(handler-bind [(TooBig [c] (set seen (+ seen (.n c))))]
|
|
(let [xs [5 200 7 300]]
|
|
(println (fold (slice xs 0 4) checked))
|
|
(println (fold (slice xs 0 4) (fn [x] (min x 10))))))
|
|
(print "seen ") (print seen) (println ""))
|
|
|
|
(defn main [] i32
|
|
(let [xs [1 2 3 4]]
|
|
;; A name in value position, passed down.
|
|
(each! (slice xs 0 4) double)
|
|
(print (at xs 0)) (print " ") (print (at xs 3)) (println "")
|
|
;; 2 + 4 + 6 + 8 negated
|
|
(println (fold (slice xs 0 4) negate))
|
|
;; An fn literal, whose parameter types come from the position it is in.
|
|
(println (fold (slice xs 0 4) (fn [x] (+ x 1))))
|
|
;; A let binding of function type, called by the name it is bound to.
|
|
(let [f square]
|
|
(println (f 9))))
|
|
|
|
;; A comparator, and the same slice sorted both ways.
|
|
(let [ys [3 1 4 1 5 9 2 6]
|
|
s (slice ys 0 8)]
|
|
(sort-by! s ascending)
|
|
(print (at s 0)) (print " ") (print (at s 7)) (println "")
|
|
(sort-by! s descending)
|
|
(print (at s 0)) (print " ") (print (at s 7)) (println "")
|
|
;; A returned function value, and a computed head calling it.
|
|
(sort-by! s (pick true))
|
|
(print (at s 0)) (println "")
|
|
(println ((pick false) 1 2)))
|
|
|
|
(handles)
|
|
0)
|