;; Infinite structures + Prelude tests. The lazy `:` operator builds ;; cons cells with thunked head/tail so recursive list-defining ;; functions terminate when only a finite prefix is consumed. (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)))) ;; ── Prelude basics ── (hk-test "head of literal" (hk-eval-expr-source "head [1, 2, 3]") 1) (hk-test "tail of literal" (hk-eval-list "tail [1, 2, 3]") (list 2 3)) (hk-test "length" (hk-eval-expr-source "length [10, 20, 30, 40]") 4) (hk-test "length empty" (hk-eval-expr-source "length []") 0) (hk-test "map with section" (hk-eval-list "map (+ 1) [1, 2, 3]") (list 2 3 4)) (hk-test "filter" (hk-eval-list "filter (\\x -> x > 2) [1, 2, 3, 4, 5]") (list 3 4 5)) (hk-test "drop" (hk-eval-list "drop 2 [10, 20, 30, 40]") (list 30 40)) (hk-test "fst" (hk-eval-expr-source "fst (7, 9)") 7) (hk-test "snd" (hk-eval-expr-source "snd (7, 9)") 9) (hk-test "zipWith" (hk-eval-list "zipWith plus [1, 2, 3] [10, 20, 30]") (list 11 22 33)) ;; ── Infinite structures ── (hk-test "take from repeat" (hk-eval-list "take 5 (repeat 7)") (list 7 7 7 7 7)) (hk-test "take 0 from repeat returns empty" (hk-eval-list "take 0 (repeat 7)") (list)) (hk-test "take from iterate" (hk-eval-list "take 5 (iterate (\\x -> x + 1) 0)") (list 0 1 2 3 4)) (hk-test "iterate with multiplication" (hk-eval-list "take 4 (iterate (\\x -> x * 2) 1)") (list 1 2 4 8)) (hk-test "head of repeat" (hk-eval-expr-source "head (repeat 99)") 99) ;; ── Fibonacci stream ── (hk-test "first 10 Fibonacci numbers" (hk-eval-list "take 10 fibs") (list 0 1 1 2 3 5 8 13 21 34)) (hk-test "fib at position 8" (hk-eval-expr-source "head (drop 8 fibs)") 21) ;; ── Building infinite structures in user code ── (hk-test "user-defined infinite ones" (hk-prog-val "ones = 1 : ones\nresult = take 6 ones" "result") (list ":" 1 (list ":" 1 (list ":" 1 (list ":" 1 (list ":" 1 (list ":" 1 (list "[]")))))))) (hk-test "user-defined nats" (hk-prog-val "nats = naturalsFrom 1\nnaturalsFrom n = n : naturalsFrom (n + 1)\nresult = take 5 nats" "result") (list ":" 1 (list ":" 2 (list ":" 3 (list ":" 4 (list ":" 5 (list "[]"))))))) ;; ── Range syntax ── (hk-test "finite range [1..5]" (hk-eval-list "[1..5]") (list 1 2 3 4 5)) (hk-test "empty range when from > to" (hk-eval-list "[10..3]") (list)) (hk-test "stepped range" (hk-eval-list "[1, 3..10]") (list 1 3 5 7 9)) (hk-test "open range — head" (hk-eval-expr-source "head [1..]") 1) (hk-test "open range — drop then head" (hk-eval-expr-source "head (drop 99 [1..])") 100) (hk-test "open range — take 5" (hk-eval-list "take 5 [10..]") (list 10 11 12 13 14)) ;; ── Composing Prelude functions ── (hk-test "map then filter" (hk-eval-list "filter (\\x -> x > 5) (map (\\x -> x * 2) [1, 2, 3, 4])") (list 6 8)) (hk-test "sum-via-foldless" (hk-prog-val "mySum [] = 0\nmySum (x:xs) = x + mySum xs\nresult = mySum (take 5 (iterate (\\x -> x + 1) 1))" "result") 15) {:fails hk-test-fails :pass hk-test-pass :fail hk-test-fail}