;;;; M2 queue item 7: dyn if tests a scrutinee's truthiness rather than ;;;; requiring a strict bool, when the scrutinee's own type is dyn. Clojure's ;;;; rule, not C's or Python's: nil and false are the only falsey values, and ;;;; everything else -- 0, "", an empty vec, an empty map, a keyword -- is ;;;; truthy. ;;;; ;;;; The rule reaches every form built out of [if] under the hood -- [when], ;;;; [cond], [and] and [or] all desugar to it in parse.ml -- so their *tests* ;;;; need no separate case in check.ml and get exercised below through their ;;;; own syntax rather than by inspecting the desugaring. [and]'s *answer* ;;;; carries a non-bool dyn value through too, Clojure-style, because its ;;;; short-circuit sentinel is the else arm and the real value's type wins. ;;;; [or]'s sentinel is the then arm instead, so it is the one that decides ;;;; the whole expression's type when nothing else does, and [or]'s answer ;;;; stays a strict bool -- a pre-existing asymmetry (parse.ml, shortcircuit) ;;;; this item leaves alone; only [or]'s tests are exercised here, not a ;;;; non-bool value in its answer position. [not] and [while] are not [if] ;;;; in disguise, so check_truthy is called at their own sites by hand, and ;;;; get their own coverage too. ;;;; ;;;; A typed if keeps needing a strict bool -- that refusal, and its message, ;;;; is a checker test in test_flan.ml, not a row here, since a program that ;;;; gave a typed if a non-bool scrutinee would not compile. ;; An unannotated parameter is always dyn, so calling this on a literal is ;; what boxes it -- the same way an argument to a dyn-typed parameter always ;; does. Used below wherever a literal has to reach a boolean position as a ;; genuine dyn value rather than as the typed value it would otherwise default ;; to (a bare 0 is an i32 until something wants it as dyn). (defn box [x] dyn x) (defn truthy? [x] dyn (if x "truthy" "falsey")) (defn main [] i32 ;; nil and false: the only two falsey dyn values. Everything else Clojure ;; calls truthy that C or Python would not: 0, "", an empty vec, an empty ;; map, a keyword. (println (truthy? nil)) (println (truthy? false)) (println (truthy? true)) (println (truthy? 0)) (println (truthy? 7)) (println (truthy? "")) (println (truthy? "x")) (println (truthy? (vec-new dyn))) (let [xs (vec-new dyn)] (push xs 1) (println (truthy? xs))) (let [m {}] (println (truthy? m))) (println (truthy? {:a 1})) (println (truthy? :kw)) ;; when: sugar for a one-armed if, so nil/false skip the body and every ;; other dyn value -- 0 and "" included -- runs it. (when (box nil) (println "when nil ran")) (when (box false) (println "when false ran")) (when (box 0) (println "when 0 ran")) (when (box "") (println "when empty-string ran")) ;; cond: each test is an if in a chain, so the same rule applies clause by ;; clause -- a boxed 0 falls through to its body just like a boxed "x". (println (cond (box nil) "a" (box 0) "b" :else "c")) (println (cond (box false) "a" (box "x") "b" :else "c")) ;; and/or: also if in disguise, so each test along the chain is ;; truthy-tested the same way if's own is -- 0 and "" do not stop and, ;; only nil and false do; 0 does stop or, the way any truthy value does. ;; and's answer is the last truthy operand itself (:kw here), Clojure's ;; and. or's answer stays a plain bool -- "true" below is or's own ;; sentinel, not the operand that made it truthy; that asymmetry is ;; parse.ml's, not this item's to close. (println (and (box 1) (box "") (box :kw))) (println (and (box 1) (box false) (box "unreached"))) (println (or (box 0) (box false))) (println (or (box nil) (box false))) ;; not: truthiness, negated -- true only for nil and false. (println (not (box nil))) (println (not (box false))) (println (not (box 0))) (println (not (box ""))) (println (not (box true))) ;; while: the loop condition is a truthiness test the same way if's is. A ;; dyn vec ending in nil stops the loop; the 0 and "" along the way, if any ;; were there, would not. (let [n (vec-new dyn)] (push n 3) (push n 2) (push n 1) (push n nil) (let [i 0] (while (at n i) (println (at n i)) (set i (+ i 1))))) 0)