flan/test/programs/nil-option.flan
Joseph Ferano a4c6b996ff def re-runs its initialiser, and defvar is renamed defonce
The trio the author decided on 2026-09-20 is now all built: def is CL's
defparameter — its initialiser runs on every daemon re-run, unguarded, so
an edited initialiser repaints the same storage on C-c C-c plus re-run —
defonce (Clojure's name for CL's defvar, per the author) initialises once
behind the .init~once. flag, and defconst stays the image.

One parse arm reads both forms; the difference is Ast.reinit, carried to
Tast.global's grerun. Emit.startup_plan gives a def no guard flag, and
Check.check_global lifts every def initialiser — zero and literal
included — into global/<n>, so the host's startup reaches it through the
function cell and a re-evaluated def swaps it (Session's def_inits;
Emit.redefinition declares the cell for a non-sibling target). The old
defvar spelling is refused with the rename and both compiling spellings,
and every program, test, doc and editor list is swept — except sand.flan,
the author's live WIP, whose seven defvar lines are flagged in FIX.org
and keep its three dependent tests red on this branch.
2026-09-21 07:12:04 +07:00

54 lines
2.2 KiB
Plaintext

;;;; nil <-> None at (Option T) boundaries -- M2 queue item 4.
;;;;
;;;; nil is dyn's own absence and None is (Option T)'s; this is the boundary
;;;; where the checker decides they are the same absence. [absent] and
;;;; [opt-of] take it in at the two annotated sites that are not a function
;;;; argument -- a global's declared type and a return type; [via-param]
;;;; takes it in at the third, a parameter. [as-dyn] is the other direction:
;;;; a written (Option i64) crossing into dyn becomes nil or the boxed
;;;; payload. [box-it]/[unbox-opt] round-trip a value through both crossings.
;;;;
;;;; The last line is the trap: a dyn that is nil only once the program runs,
;;;; reaching a bare i64. The literal [nil] two lines above it would have been
;;;; refused at compile time instead -- see test_flan.ml and
;;;; test_acceptance.ml's "nil at a bare T, compile time" row for that half.
(defonce absent (Option i64) nil)
(defn opt-of [flag bool] (Option i64)
(if flag (Some 7) nil))
(defn via-param [o (Option i64)] i64
(match o (Some v) v None -1))
(defn as-dyn [o (Option i64)] dyn o)
(defn box-it [x i64] dyn x)
(defn unbox-opt [d dyn] (Option i64) d)
(defn maybe-nil [flag bool] dyn (if flag 5 nil))
(defn take-i64 [n i64] i64 n)
(defn show [o (Option i64)] ()
(print (match o (Some v) v None -1)) (println ""))
(defn main [] ()
;; nil -> None, at a global's declared type, a return type and a parameter.
(show absent) ; -1
(show (opt-of true)) ; 7
(show (opt-of false)) ; -1
(print (via-param nil)) (println "") ; -1
(print (via-param (Some 3))) (println "") ; 3
;; None -> nil, crossing into dyn; Some x -> the boxed x.
(print (= (as-dyn None) nil)) (println "") ; true
(print (as-dyn (Some 9))) (println "") ; 9
;; A value round-tripped through both crossings: typed -> dyn -> (Option T).
(show (unbox-opt (box-it 42))) ; 42
;; The trap: a dyn that turns out to be nil only when the program runs,
;; reaching a bare i64. flan_dyn_need_i64 owns the wording.
(print (take-i64 (maybe-nil false)))
(println ""))