72 lines
3.0 KiB
Plaintext
72 lines
3.0 KiB
Plaintext
;;;; Converting between an enum and an integer, both ways, explicitly.
|
|
;;;;
|
|
;;;; An enum is an i32 at run time and its own type in the checker. That is
|
|
;;;; what makes :spcae an error at the call site rather than a wrong number
|
|
;;;; later, and it is deliberately not weakened here: a bare integer still
|
|
;;;; does not fit an enum parameter, and a keyword still resolves against the
|
|
;;;; enum the site expects. What is added is a way to *say* the conversion,
|
|
;;;; by name, where it is meant — (i32 k) and (K n).
|
|
;;;;
|
|
;;;; Both directions are free. emit.ml's cast reduces an enum to its i32
|
|
;;;; before choosing an instruction, so src and target are the same type and
|
|
;;;; the value is answered unchanged; there is no instruction to see at -O0
|
|
;;;; either, which is why this runs at both optimisation levels.
|
|
|
|
(defenum K [lo -1 mid 0 hi 1])
|
|
|
|
(defn num [k K] i32 (i32 k))
|
|
|
|
;; An enum parameter driven by a loop variable, which is the shape this exists
|
|
;; for: the caller has an index, not a member.
|
|
(defn name-at [i i32] string
|
|
(let [k (K i)]
|
|
(cond (= k :lo) "lo"
|
|
(= k :mid) "mid"
|
|
(= k :hi) "hi"
|
|
:else "other")))
|
|
|
|
(defn main [] i32
|
|
;; enum → i32. Lossless by construction: i32 is the representation.
|
|
(println (num :lo))
|
|
(println (num :mid))
|
|
(println (num :hi))
|
|
|
|
;; i32 → enum, then back. A round trip is the identity both ways because
|
|
;; neither direction is a conversion at run time.
|
|
(println (num (K 1)))
|
|
(println (i32 (K (num :lo))))
|
|
|
|
;; A value that is no declared member is allowed. raylib's gesture bitfield
|
|
;; is an OR of flags and is exactly this, and the printer already falls
|
|
;; through to the number for an out-of-range enum, so refusing to construct
|
|
;; one while agreeing to print it would be incoherent.
|
|
(let [odd (K 7)]
|
|
(println (num odd))
|
|
;; And printed as itself, which is the half that makes this coherent: the
|
|
;; structural printer is a comparison chain over the declared members and
|
|
;; falls through to the number when none match. Agreeing to show a value
|
|
;; outside the members while refusing to build one would be the
|
|
;; incoherence.
|
|
(println odd)
|
|
;; The contrast: a value that IS a member prints as the member.
|
|
(println (K 1))
|
|
(println (if (= odd :hi) "member" "not a member"))
|
|
;; The comparisons the raw bitfield wants, which need nothing beyond the
|
|
;; conversion: the enum goes to i32 and the literal follows it.
|
|
(println (if (> (i32 odd) 3) "above 3" "not above 3")))
|
|
|
|
;; A narrower or wider target truncates and extends by the same rule every
|
|
;; int→int cast follows — lo is -1, so i64 sign-extends and u8 wraps.
|
|
(println (i64 (K -1)))
|
|
(println (u8 (K -1)))
|
|
|
|
;; A float target means (f32 (i32 k)); nothing special. Written as (K 1)
|
|
;; rather than :hi because a bare keyword outside an enum-typed position has
|
|
;; no enum to resolve against, and that refusal is unchanged.
|
|
(println (f32 (K 1)))
|
|
|
|
;; And the index-driven loop the whole thing is for.
|
|
(dotimes [i 4]
|
|
(println (name-at (- i 1))))
|
|
0)
|