diff --git a/plan.org b/plan.org index d0aa2af..1b21c96 100644 --- a/plan.org +++ b/plan.org @@ -194,8 +194,10 @@ and on a managed ~class~ instance. An ordinary ~struct~ never carries one. - Types are mandatory; *inference* makes them feel optional. Annotate function signatures, infer locals — Odin/Zig/Rust ergonomics. -- Signatures are annotated as inline name/type pairs, as in ~let~ and - ~defstruct~: ~(defn area [s Shape] f32 ...)~. No separate ~declare~ form — +- Signatures are annotated as inline name/type pairs, as in ~defstruct~ and a + ~restart-case~ clause: ~(defn area [s Shape] f32 ...)~. A ~let~ is not one of + them — a local is inferred from its initialiser and takes no annotation at + all. No separate ~declare~ form — ~declare~ is kept only where there is no body (forward declarations, FFI). - Annotations at function boundaries are unavoidable, because compile-time overloading is incompatible with full inference. Locals are inferred. diff --git a/spec-memory.md b/spec-memory.md index f13eb43..c10fac4 100644 --- a/spec-memory.md +++ b/spec-memory.md @@ -166,6 +166,12 @@ instead: (defn largest [xs [$t] gt (Fn [$t $t] bool)] (Option $t) ...) ``` +The value handed to such a parameter is a named `defn`. An `fn` cannot be written +inline into it, because the generic body is checked with nothing substituted and +there is no concrete type yet for the `fn`'s own parameters to come from; that +restriction lifts at a monomorphic call site, where `reduce`'s and `filter`'s +callbacks are ordinary inline `fn`s. + The alternatives to predicates — compile-time interfaces, or intrinsics restricted to primitives — remain deliberately deferred until the base checker is stable (build sequence milestone 4). The ceiling is that nobody can supply a diff --git a/syntax-sketch.flan b/syntax-sketch.flan index 95c4332..78e30fe 100644 --- a/syntax-sketch.flan +++ b/syntax-sketch.flan @@ -2,11 +2,13 @@ ;; ;; Rules held here: ;; - every type notation reads as exactly ONE data item -;; - types are inline name/type pairs, as in `let` and `defstruct` +;; - types are inline name/type pairs, as in `defstruct` and a restart-case +;; clause. NOT in `let`: a local is inferred and takes no annotation ;; - the return type is always written; () is unit, a real zero-sized type ;; rather than C's void -;; - lowercase type names are variables, Capitalized are concrete -;; - no `!` convention (nothing is immutable), no `->`, no sigils +;; - a type VARIABLE is $t; every other type name is concrete, whatever its +;; case. Lowercase-is-a-variable was the first spelling and is gone +;; - no `!` convention (nothing is immutable), and no `->` ;; ;; Normative references: spec-memory.md (ownership, containers, places, ;; generics, function values) and spec-conditions.md (restart semantics). @@ -91,8 +93,8 @@ ;; >". 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. +;; named defn; at a monomorphic call site, where the types are already +;; concrete, the fn can be written inline where it is used. ;; Parameters are immutable values; pass a pointer to mutate. `[Enemy]` is a ;; borrowed slice — centroid neither owns nor frees the storage. @@ -151,7 +153,9 @@ ;; load-texture cannot know the right recovery — an editor wants a placeholder, ;; a release build wants to abort, a hot-reload session wants to retry after the ;; file is fixed on disk. So it offers a menu and the caller chooses. -(defcondition AssetMissing [path string]) +;; A condition type is an ordinary struct — there is no defcondition, and no +;; class hierarchy to put one in. Matching is by type plus a predicate. +(defstruct AssetMissing [path string]) ;; `signal` has type () and RETURNS if every handler returns normally, so the ;; fall-through path of a restart-case in value position must still produce the @@ -168,10 +172,14 @@ ;; Intermediate frames say nothing about AssetMissing. Nothing to thread. ;; invoke-restart has type Never: it does not return to the handler. -(defn load-level [path string] () Level - (handler-bind [AssetMissing (fn [c] - (log "missing asset:" (.path c)) - (invoke-restart 'use-placeholder))] +;; A handler clause is (Type [name] body ...) — the type, then the one binding, +;; then the body. It is not a type paired with an `fn`, and a handler closes +;; over nothing: it is lifted into its own function, so a value it wants to keep +;; goes on the condition or into a global. +(defn load-level [path string] Level + (handler-bind [(AssetMissing [c] + (log "missing asset:" (.path c)) + (invoke-restart 'use-placeholder))] (parse-level (slurp path)))) ;; A handler that returns normally does not unwind, so the signaller carries on. @@ -189,10 +197,9 @@ (defn collect-parse-errors [src string] (Result Ast) (let [errors (make-vec ParseError)] - (handler-bind [ParseError (fn [c] - (push errors c) ; value struct: copies out of - ; the signalling frame - (invoke-restart 'skip-form))] + (handler-bind [(ParseError [c] + (push errors c) ; value struct: copies out of + (invoke-restart 'skip-form))] ; the signalling frame (let [ast (parse-all (parser src))] (if (zero? (len errors)) (Ok ast)