;; seq / deepseq tests. seq is strict in its first arg (forces to ;; WHNF) and returns the second arg unchanged. deepseq additionally ;; forces the first arg to normal form. (define hk-prog-val (fn (src name) (hk-deep-force (get (hk-eval-program (hk-core src)) name)))) (define hk-as-list (fn (xs) (cond ((and (list? xs) (= (first xs) "[]")) (list)) ((and (list? xs) (= (first xs) ":")) (cons (nth xs 1) (hk-as-list (nth xs 2)))) (:else xs)))) (define hk-eval-list (fn (src) (hk-as-list (hk-eval-expr-source src)))) ;; ── seq returns its second arg ── (hk-test "seq with primitive first arg" (hk-eval-expr-source "seq 1 99") 99) (hk-test "seq forces first arg via let" (hk-eval-expr-source "let x = 1 + 2 in seq x x") 3) (hk-test "seq second arg is whatever shape" (hk-eval-expr-source "seq 0 \"hello\"") "hello") ;; ── seq enables previously-lazy bottom to be forced ── ;; Without seq the let-binding `x = error …` is never forced; ;; with seq it must be forced because seq is strict in its first ;; argument. We don't run that error case here (it would terminate ;; the test), but we do verify the negative — that without seq, ;; the bottom bound is never demanded. (hk-test "lazy let — bottom never forced when unused" (hk-eval-expr-source "let x = error \"never\" in 42") 42) ;; ── deepseq forces nested structure ── (hk-test "deepseq with finite list" (hk-eval-expr-source "deepseq [1, 2, 3] 7") 7) (hk-test "deepseq with constructor value" (hk-eval-expr-source "deepseq (Just 5) 11") 11) (hk-test "deepseq with tuple" (hk-eval-expr-source "deepseq (1, 2) 13") 13) ;; ── seq + arithmetic ── (hk-test "seq used inside arithmetic doesn't poison the result" (hk-eval-expr-source "(seq 1 5) + (seq 2 7)") 12) ;; ── seq in user code ── (hk-test "seq via fun-clause" (hk-prog-val "f x = seq x (x + 1)\nresult = f 10" "result") 11) (hk-test "seq sequences list construction" (hk-eval-list "[seq 1 10, seq 2 20]") (list 10 20)) {:fails hk-test-fails :pass hk-test-pass :fail hk-test-fail}