diff --git a/syntax-sketch.flan b/syntax-sketch.flan index c533067..95c4332 100644 --- a/syntax-sketch.flan +++ b/syntax-sketch.flan @@ -31,8 +31,8 @@ ;; $t)}, is the other brace form, and it sits after the return type. ;; (Ptr World) pointer ;; (Fn [f32] bool) function pointer, no captured environment -;; (Option a) union from the stdlib -;; (Handle a) generational handle into a pool +;; (Option $t) union from the stdlib +;; (Handle $t) generational handle into a pool ;; ;; A struct is a value type iff all its fields are. One Vec field makes it ;; move-only. Copying an owning container is always explicit: (clone v). @@ -57,22 +57,51 @@ (Circle r) (* PI r r) (Rect w h) (* w h))) -;; ── Lowercase = type variable. Monomorphised at each call site ──────── -;; There are no type classes, so `a` supports only what EVERY type supports. -;; Ordering is not that — it is passed in as a function value. Type arguments -;; are inferred from the argument types; there is no explicit instantiation. -;; The inner `fn` captures `gt`, a parameter: legal because it does not outlive -;; this frame (spec-memory.md, non-escaping fn). -(defn largest [xs [a] gt (Fn [a a] bool)] (Option a) +;; ── $t binds a type variable. Monomorphised at each call site ───────── +;; The sigil is on the type, everywhere a type goes: [$t], (Fn [$t $t] bool), +;; (Option $t). Bare t is the same variable where a type's NAME is an argument +;; in expression position — (vec-new t), (map-new t i32), the cast (t x). +;; There are no type classes, so $t supports only what EVERY type supports, and +;; the body is checked abstractly, so an unsupported operation is an error here +;; rather than at the first call site that happened to instantiate it. Ordering +;; is not supported, so it is passed in as a function value. Type arguments are +;; inferred from the argument types; there is no explicit instantiation. +(defn largest [xs [$t] gt (Fn [$t $t] bool)] (Option $t) + {:where (copyable? $t)} (if (> (len xs) 0) - (Some (reduce (fn [x y] (if (gt x y) x y)) (at xs 0) xs)) + (let [best (at xs 0)] + (dotimes [i (len xs)] + (when (gt (at xs i) best) (set best (at xs i)))) + (Some best)) None)) -;; (largest hps >) — `>` at i32 is an ordinary function value -;; (largest es (fn [x y] (> (.hp x) (.hp y)))) +;; A {:where ...} clause admits the operator instead of taking it as an +;; argument. Five predicates — ordered? equal? hashable? numeric? copyable? — +;; and each instantiation is checked against the ones the signature declares. +(defn smallest [xs [$t]] (Option $t) + {:where (ordered? $t)} + (if (> (len xs) 0) + (let [m (at xs 0)] + (dotimes [i (len xs)] (set m (min m (at xs i)))) + (Some m)) + None)) + +;; (largest hps taller) — a top-level defn is an ordinary function value. +;; An OPERATOR is not: `>` is not a name, and (largest hps >) is "unknown name +;; >". Nor can an `fn` be written inline into a (Fn [$t $t] bool) argument: the +;; generic body is checked with nothing substituted, so there is no type for the +;; fn's own parameters to come from yet. Inside a generic the callback is a +;; named defn; at a monomorphic call site the fn can be written where it is +;; used, as `centroid` does below. ;; Parameters are immutable values; pass a pointer to mutate. `[Enemy]` is a ;; borrowed slice — centroid neither owns nor frees the storage. +;; This one is still a sketch of where the syntax is going and does not compile +;; today, on two counts worth naming rather than leaving to be discovered: +;; component-wise `+` and `/` over a fixed array are planned and not built, and +;; the prelude's `reduce` is (reduce s init f) with its accumulator at the +;; ELEMENT type, so it cannot fold an [Enemy] into a Vec2. Written against what +;; exists, this is a `dotimes` accumulating into a local. (defn centroid [es [Enemy]] Vec2 (/ (reduce (fn [acc e] (+ acc (.pos e))) [0 0] es) (f32 (len es))))