; Iter 15b — second stdlib module: polymorphic singly-linked lists.
; Imports std_maybe so head/tail can return Maybe<a> / Maybe<List<a>>.
; All cross-module references use the qualified form per the Iter 14h
; convention: `std_maybe.Maybe` at type-name slots, `std_maybe.from_maybe`
; for fns. Bare ctor names (`Just`, `Nothing`) stay unqualified — once
; the type is resolved the ctor lookup is unambiguous.

(module std_list

  (import std_maybe)

  (data List (vars a)
    (doc "Polymorphic singly-linked list: Nil or Cons<a, List<a>>.")
    (ctor Nil)
    (ctor Cons a (con List a)))

  (fn fold_left
    (doc "Tail-recursive left fold. The recursive call is in tail position and is marked.")
    (type
      (forall (vars a b)
        (fn-type
          (params (fn-type (params b a) (ret b))
                  b
                  (con List a))
          (ret b))))
    (params f acc xs)
    (body
      (match xs
        (case (pat-ctor Nil) acc)
        (case (pat-ctor Cons h t)
          (tail-app fold_left f (app f acc h) t)))))

  (fn fold_right
    (doc "Right fold. Constructor-blocked: the recursive call is the second arg of f, NOT a tail position.")
    (type
      (forall (vars a b)
        (fn-type
          (params (fn-type (params a b) (ret b))
                  b
                  (con List a))
          (ret b))))
    (params f acc xs)
    (body
      (match xs
        (case (pat-ctor Nil) acc)
        (case (pat-ctor Cons h t)
          (app f h (app fold_right f acc t))))))

  (fn length
    (doc "Length via fold_left. The accumulating lambda ignores the element and increments the counter.")
    (type
      (forall (vars a)
        (fn-type
          (params (con List a))
          (ret (con Int)))))
    (params xs)
    (body
      (app fold_left
        (lam (params (typed c (con Int)) (typed _ a))
             (ret (con Int))
             (body (app + c 1)))
        0
        xs)))

  (fn reverse
    (doc "Reverse via fold_left with a flipping accumulator. Tail-recursive by virtue of the fold_left call.")
    (type
      (forall (vars a)
        (fn-type
          (params (con List a))
          (ret (con List a)))))
    (params xs)
    (body
      (app fold_left
        (lam (params (typed acc (con List a)) (typed h a))
             (ret (con List a))
             (body (term-ctor List Cons h acc)))
        (term-ctor List Nil)
        xs)))

  (fn is_empty
    (doc "True iff the list is Nil.")
    (type
      (forall (vars a)
        (fn-type
          (params (con List a))
          (ret (con Bool)))))
    (params xs)
    (body
      (match xs
        (case (pat-ctor Nil) true)
        (case (pat-ctor Cons _ _) false))))

  (fn head
    (doc "Returns Just<a> of the first element, or Nothing for an empty list. Cross-module Maybe.")
    (type
      (forall (vars a)
        (fn-type
          (params (con List a))
          (ret (con std_maybe.Maybe a)))))
    (params xs)
    (body
      (match xs
        (case (pat-ctor Nil) (term-ctor std_maybe.Maybe Nothing))
        (case (pat-ctor Cons h _) (term-ctor std_maybe.Maybe Just h)))))

  (fn tail
    (doc "Returns Just<List<a>> of the tail, or Nothing for an empty list.")
    (type
      (forall (vars a)
        (fn-type
          (params (con List a))
          (ret (con std_maybe.Maybe (con List a))))))
    (params xs)
    (body
      (match xs
        (case (pat-ctor Nil) (term-ctor std_maybe.Maybe Nothing))
        (case (pat-ctor Cons _ t) (term-ctor std_maybe.Maybe Just t)))))

  (fn append
    (doc "Concatenate two lists. Constructor-blocked recursion: the recursive call is inside Cons, NOT a tail.")
    (type
      (forall (vars a)
        (fn-type
          (params (con List a) (con List a))
          (ret (con List a)))))
    (params xs ys)
    (body
      (match xs
        (case (pat-ctor Nil) ys)
        (case (pat-ctor Cons h t)
          (term-ctor List Cons h (app append t ys))))))

  (fn map
    (doc "Polymorphic map. Constructor-blocked recursion.")
    (type
      (forall (vars a b)
        (fn-type
          (params (fn-type (params a) (ret b))
                  (con List a))
          (ret (con List b)))))
    (params f xs)
    (body
      (match xs
        (case (pat-ctor Nil) (term-ctor List Nil))
        (case (pat-ctor Cons h t)
          (term-ctor List Cons (app f h) (app map f t))))))

  (fn filter
    (doc "Keep elements where p is true. Constructor-blocked when p holds; non-tail recursive call when p is false.")
    (type
      (forall (vars a)
        (fn-type
          (params (fn-type (params a) (ret (con Bool)))
                  (con List a))
          (ret (con List a)))))
    (params p xs)
    (body
      (match xs
        (case (pat-ctor Nil) (term-ctor List Nil))
        (case (pat-ctor Cons h t)
          (if (app p h)
            (term-ctor List Cons h (app filter p t))
            (app filter p t))))))

  (fn take
    (doc "First n elements of xs (or all of xs if it has fewer than n).")
    (type
      (forall (vars a)
        (fn-type
          (params (con Int) (con List a))
          (ret (con List a)))))
    (params n xs)
    (body
      (if (app <= n 0)
        (term-ctor List Nil)
        (match xs
          (case (pat-ctor Nil) (term-ctor List Nil))
          (case (pat-ctor Cons h t)
            (term-ctor List Cons h (app take (app - n 1) t)))))))

  (fn drop
    (doc "All elements of xs after the first n (or Nil if xs has fewer than n elements).")
    (type
      (forall (vars a)
        (fn-type
          (params (con Int) (con List a))
          (ret (con List a)))))
    (params n xs)
    (body
      (if (app <= n 0)
        xs
        (match xs
          (case (pat-ctor Nil) (term-ctor List Nil))
          (case (pat-ctor Cons _ t)
            (app drop (app - n 1) t)))))))
