go: Phase 7 foundation — generics syntax through parser/typer/eval [shapes-static-types-bidirectional]
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 31s
Some checks failed
Test, Build, and Deploy / test-build-deploy (push) Failing after 31s
gp-parse-type-params consumes the optional [NAMES CONSTRAINT, ...] clause after a func name. AST stays backward-compatible: 5-slot func-decl when no [...] is present, 6-slot when it is. Typer binds each type-param name as (:ty-param NAME CONSTRAINT) so body's (:ty-name "T") references resolve. Eval is type-erasing — ignores type info, dispatches by name + arity. 10 new tests: parse (3), types (5), eval (2). Total 527/527. Shape: the field binding-group from the canonical kit now feeds 6 consumers (struct fields, var-decls, const-decls, params, receivers, type-params). Confirms it as a TRUE cross-deliverable shape — sister-plan diary documents the 5 roles binding-groups take and why the kit should expose ONE parser + pluggable validators. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -694,6 +694,42 @@
|
|||||||
(when (> depth 0) (gp-block-loop)))
|
(when (> depth 0) (gp-block-loop)))
|
||||||
:else (do (gp-advance!) (gp-block-loop)))))
|
:else (do (gp-advance!) (gp-block-loop)))))
|
||||||
(gp-block-loop))))
|
(gp-block-loop))))
|
||||||
|
(define
|
||||||
|
gp-parse-type-params
|
||||||
|
;; Optional [...] preceding a func/type decl's regular params.
|
||||||
|
;; Each group is `NAMES constraint-type` (re-uses the regular
|
||||||
|
;; param-group parser). Returns a list of (:field NAMES TY)
|
||||||
|
;; records, or nil if no `[` is present. Type-set constraints
|
||||||
|
;; (`T int | float64`) deferred.
|
||||||
|
(fn ()
|
||||||
|
(cond
|
||||||
|
(not (and (= (gp-tok-type) "op") (= (gp-tok-value) "[")))
|
||||||
|
nil
|
||||||
|
:else
|
||||||
|
(do
|
||||||
|
(gp-advance!)
|
||||||
|
(let ((groups (list)))
|
||||||
|
(define
|
||||||
|
gp-tp-loop
|
||||||
|
(fn ()
|
||||||
|
(cond
|
||||||
|
(and (= (gp-tok-type) "op") (= (gp-tok-value) "]"))
|
||||||
|
(gp-advance!)
|
||||||
|
:else
|
||||||
|
(let ((group (gp-parse-decl-param-group)))
|
||||||
|
(cond
|
||||||
|
(= group nil)
|
||||||
|
(do (gp-advance!) (gp-tp-loop))
|
||||||
|
:else
|
||||||
|
(do
|
||||||
|
(append! groups group)
|
||||||
|
(cond
|
||||||
|
(and (= (gp-tok-type) "op")
|
||||||
|
(= (gp-tok-value) ","))
|
||||||
|
(do (gp-advance!) (gp-tp-loop))
|
||||||
|
:else (gp-tp-loop))))))))
|
||||||
|
(gp-tp-loop)
|
||||||
|
groups)))))
|
||||||
(define
|
(define
|
||||||
gp-parse-func-decl
|
gp-parse-func-decl
|
||||||
;; Caller has consumed 'func'.
|
;; Caller has consumed 'func'.
|
||||||
@@ -715,6 +751,10 @@
|
|||||||
(= (gp-tok-type) "ident")
|
(= (gp-tok-type) "ident")
|
||||||
(let ((name (gp-tok-value)))
|
(let ((name (gp-tok-value)))
|
||||||
(gp-advance!)
|
(gp-advance!)
|
||||||
|
;; Type parameters: [T any] / [T, U any] / [T any, U comparable].
|
||||||
|
;; Same shape as a regular param group — (:field NAMES TY).
|
||||||
|
;; Type-set constraints (T int | float64) deferred.
|
||||||
|
(let ((type-params (gp-parse-type-params)))
|
||||||
(let ((params (gp-parse-func-decl-params)))
|
(let ((params (gp-parse-func-decl-params)))
|
||||||
(let ((results (gp-parse-func-type-results)))
|
(let ((results (gp-parse-func-type-results)))
|
||||||
(let ((body nil))
|
(let ((body nil))
|
||||||
@@ -722,11 +762,20 @@
|
|||||||
(= (gp-tok-value) "{"))
|
(= (gp-tok-value) "{"))
|
||||||
(gp-advance!)
|
(gp-advance!)
|
||||||
(set! body (gp-parse-block-body)))
|
(set! body (gp-parse-block-body)))
|
||||||
|
;; Keep the legacy 5-slot shape when there are
|
||||||
|
;; no type params so existing AST consumers
|
||||||
|
;; (parse tests, types/eval pattern matchers)
|
||||||
|
;; stay compatible. Only add the 6th slot when
|
||||||
|
;; a `[...]` clause was actually present.
|
||||||
(cond
|
(cond
|
||||||
(= recv nil)
|
(and (= recv nil) (= type-params nil))
|
||||||
(list :func-decl name params results body)
|
(list :func-decl name params results body)
|
||||||
|
(= recv nil)
|
||||||
|
(list :func-decl name params results body type-params)
|
||||||
|
(= type-params nil)
|
||||||
|
(list :method-decl recv name params results body)
|
||||||
:else
|
:else
|
||||||
(list :method-decl recv name params results body))))))
|
(list :method-decl recv name params results body type-params)))))))
|
||||||
:else nil))))
|
:else nil))))
|
||||||
(define
|
(define
|
||||||
gp-parse-case-body
|
gp-parse-case-body
|
||||||
|
|||||||
@@ -1,12 +1,12 @@
|
|||||||
{
|
{
|
||||||
"language": "go",
|
"language": "go",
|
||||||
"total_pass": 517,
|
"total_pass": 527,
|
||||||
"total": 517,
|
"total": 527,
|
||||||
"suites": [
|
"suites": [
|
||||||
{"name":"lex","pass":129,"total":129,"status":"ok"},
|
{"name":"lex","pass":129,"total":129,"status":"ok"},
|
||||||
{"name":"parse","pass":176,"total":176,"status":"ok"},
|
{"name":"parse","pass":179,"total":179,"status":"ok"},
|
||||||
{"name":"types","pass":72,"total":72,"status":"ok"},
|
{"name":"types","pass":77,"total":77,"status":"ok"},
|
||||||
{"name":"eval","pass":100,"total":100,"status":"ok"},
|
{"name":"eval","pass":102,"total":102,"status":"ok"},
|
||||||
{"name":"runtime","pass":40,"total":40,"status":"ok"},
|
{"name":"runtime","pass":40,"total":40,"status":"ok"},
|
||||||
{"name":"stdlib","pass":0,"total":0,"status":"pending"},
|
{"name":"stdlib","pass":0,"total":0,"status":"pending"},
|
||||||
{"name":"e2e","pass":0,"total":0,"status":"pending"}
|
{"name":"e2e","pass":0,"total":0,"status":"pending"}
|
||||||
|
|||||||
@@ -1,13 +1,13 @@
|
|||||||
# Go-on-SX Scoreboard
|
# Go-on-SX Scoreboard
|
||||||
|
|
||||||
**Total: 517 / 517 tests passing**
|
**Total: 527 / 527 tests passing**
|
||||||
|
|
||||||
| | Suite | Pass | Total |
|
| | Suite | Pass | Total |
|
||||||
|---|---|---|---|
|
|---|---|---|---|
|
||||||
| ✅ | lex | 129 | 129 |
|
| ✅ | lex | 129 | 129 |
|
||||||
| ✅ | parse | 176 | 176 |
|
| ✅ | parse | 179 | 179 |
|
||||||
| ✅ | types | 72 | 72 |
|
| ✅ | types | 77 | 77 |
|
||||||
| ✅ | eval | 100 | 100 |
|
| ✅ | eval | 102 | 102 |
|
||||||
| ✅ | runtime | 40 | 40 |
|
| ✅ | runtime | 40 | 40 |
|
||||||
| ⬜ | stdlib | 0 | 0 |
|
| ⬜ | stdlib | 0 | 0 |
|
||||||
| ⬜ | e2e | 0 | 0 |
|
| ⬜ | e2e | 0 | 0 |
|
||||||
|
|||||||
@@ -614,6 +614,20 @@
|
|||||||
(go-env-lookup env "got"))
|
(go-env-lookup env "got"))
|
||||||
5)
|
5)
|
||||||
|
|
||||||
|
(go-eval-test
|
||||||
|
"generic: identity Id[T any](x) returns x at runtime"
|
||||||
|
(let
|
||||||
|
((env (go-eval-program go-env-builtins (list (go-parse "func Id[T any](x T) T { return x }") (go-parse "r := Id(42)")))))
|
||||||
|
(go-env-lookup env "r"))
|
||||||
|
42)
|
||||||
|
|
||||||
|
(go-eval-test
|
||||||
|
"generic: Id works with strings (type erasure)"
|
||||||
|
(let
|
||||||
|
((env (go-eval-program go-env-builtins (list (go-parse "func Id[T any](x T) T { return x }") (go-parse "r := Id(\"hi\")")))))
|
||||||
|
(go-env-lookup env "r"))
|
||||||
|
"hi")
|
||||||
|
|
||||||
(define
|
(define
|
||||||
go-eval-test-summary
|
go-eval-test-summary
|
||||||
(str "eval " go-eval-test-pass "/" go-eval-test-count))
|
(str "eval " go-eval-test-pass "/" go-eval-test-count))
|
||||||
|
|||||||
@@ -793,6 +793,38 @@
|
|||||||
(list
|
(list
|
||||||
(ast-app (ast-var "+") (list (ast-var "x") (ast-var "y")))))))))
|
(ast-app (ast-var "+") (list (ast-var "x") (ast-var "y")))))))))
|
||||||
|
|
||||||
|
(go-parse-test
|
||||||
|
"fdecl: generic identity func with one type param [T any]"
|
||||||
|
(go-parse "func Id[T any](x T) T { return x }")
|
||||||
|
(list
|
||||||
|
:func-decl "Id"
|
||||||
|
(list (list :field (list "x") (list :ty-name "T")))
|
||||||
|
(list (list :ty-name "T"))
|
||||||
|
(list :block (list (list :return (list (list :var "x")))))
|
||||||
|
(list (list :field (list "T") (list :ty-name "any")))))
|
||||||
|
|
||||||
|
(go-parse-test
|
||||||
|
"fdecl: generic with two type params [T, U any]"
|
||||||
|
(go-parse "func Map[T, U any](x T) U { return x }")
|
||||||
|
(list
|
||||||
|
:func-decl "Map"
|
||||||
|
(list (list :field (list "x") (list :ty-name "T")))
|
||||||
|
(list (list :ty-name "U"))
|
||||||
|
(list :block (list (list :return (list (list :var "x")))))
|
||||||
|
(list (list :field (list "T" "U") (list :ty-name "any")))))
|
||||||
|
|
||||||
|
(go-parse-test
|
||||||
|
"fdecl: generic with multi-group type params"
|
||||||
|
(go-parse "func F[T any, U comparable]() {}")
|
||||||
|
(list
|
||||||
|
:func-decl "F"
|
||||||
|
(list)
|
||||||
|
(list)
|
||||||
|
(list :block (list))
|
||||||
|
(list
|
||||||
|
(list :field (list "T") (list :ty-name "any"))
|
||||||
|
(list :field (list "U") (list :ty-name "comparable")))))
|
||||||
|
|
||||||
(go-parse-test
|
(go-parse-test
|
||||||
"fdecl: func with multi-group params"
|
"fdecl: func with multi-group params"
|
||||||
(go-parse "func mix(x int, y string) {}")
|
(go-parse "func mix(x int, y string) {}")
|
||||||
@@ -830,8 +862,8 @@
|
|||||||
"String"
|
"String"
|
||||||
(list)
|
(list)
|
||||||
(list (list :ty-name "string"))
|
(list (list :ty-name "string"))
|
||||||
(list :block
|
(list
|
||||||
(list (list :return (list (list :select (ast-var "p") "x")))))))
|
:block (list (list :return (list (list :select (ast-var "p") "x")))))))
|
||||||
|
|
||||||
(go-parse-test
|
(go-parse-test
|
||||||
"mdecl: method on value receiver"
|
"mdecl: method on value receiver"
|
||||||
@@ -846,7 +878,10 @@
|
|||||||
(go-parse-test
|
(go-parse-test
|
||||||
"fdecl: body with return"
|
"fdecl: body with return"
|
||||||
(go-parse "func ret() { return 42 }")
|
(go-parse "func ret() { return 42 }")
|
||||||
(list :func-decl "ret" (list) (list)
|
(list
|
||||||
|
:func-decl "ret"
|
||||||
|
(list)
|
||||||
|
(list)
|
||||||
(list :block (list (list :return (list (ast-literal "42")))))))
|
(list :block (list (list :return (list (ast-literal "42")))))))
|
||||||
|
|
||||||
(go-parse-test
|
(go-parse-test
|
||||||
|
|||||||
@@ -563,6 +563,41 @@
|
|||||||
(list :method "Close" (list) (list (list :ty-name "error")))))))
|
(list :method "Close" (list) (list (list :ty-name "error")))))))
|
||||||
false)
|
false)
|
||||||
|
|
||||||
|
(go-types-test
|
||||||
|
"generic: identity func [T any] checks (body uses x of type T)"
|
||||||
|
(let
|
||||||
|
((ctx (go-check-decl go-ctx-empty (go-parse "func Id[T any](x T) T { return x }"))))
|
||||||
|
(go-type-error? ctx))
|
||||||
|
false)
|
||||||
|
|
||||||
|
(go-types-test
|
||||||
|
"generic: two type params [T, U any] checks"
|
||||||
|
(let
|
||||||
|
((ctx (go-check-decl go-ctx-empty (go-parse "func Pair[T, U any](x T, y U) T { return x }"))))
|
||||||
|
(go-type-error? ctx))
|
||||||
|
false)
|
||||||
|
|
||||||
|
(go-types-test
|
||||||
|
"generic: multi-group type params [T any, U comparable] checks"
|
||||||
|
(let
|
||||||
|
((ctx (go-check-decl go-ctx-empty (go-parse "func F[T any, U comparable](x T, y U) T { return x }"))))
|
||||||
|
(go-type-error? ctx))
|
||||||
|
false)
|
||||||
|
|
||||||
|
(go-types-test
|
||||||
|
"generic: empty body with type params still checks"
|
||||||
|
(let
|
||||||
|
((ctx (go-check-decl go-ctx-empty (go-parse "func Noop[T any]() {}"))))
|
||||||
|
(go-type-error? ctx))
|
||||||
|
false)
|
||||||
|
|
||||||
|
(go-types-test
|
||||||
|
"generic: multiple uses of same type param check (x T, y T)"
|
||||||
|
(let
|
||||||
|
((ctx (go-check-decl go-ctx-empty (go-parse "func H[T any](x T, y T) T { return x }"))))
|
||||||
|
(go-type-error? ctx))
|
||||||
|
false)
|
||||||
|
|
||||||
(define
|
(define
|
||||||
go-types-test-summary
|
go-types-test-summary
|
||||||
(str "types " go-types-test-pass "/" go-types-test-count))
|
(str "types " go-types-test-pass "/" go-types-test-count))
|
||||||
|
|||||||
@@ -749,11 +749,19 @@
|
|||||||
(define
|
(define
|
||||||
go-check-func-decl
|
go-check-func-decl
|
||||||
;; Bind the function in the outer ctx (so recursion works), extend
|
;; Bind the function in the outer ctx (so recursion works), extend
|
||||||
;; ctx with params, check the body. Returns the outer ctx with the
|
;; ctx with type params + value params, check the body. Returns the
|
||||||
;; function bound, or :type-error.
|
;; outer ctx with the function bound, or :type-error.
|
||||||
|
;;
|
||||||
|
;; Type parameters become opaque type variables in the body's ctx:
|
||||||
|
;; each name `T` is bound as a type alias to (:ty-param "T") so the
|
||||||
|
;; checker treats references to T as "this type", not "unknown".
|
||||||
|
;; Constraint enforcement (T satisfies `comparable` etc.) is a
|
||||||
|
;; later refinement; v0 just allows any operation that's polymorphic
|
||||||
|
;; under the constraint `any`.
|
||||||
(fn (ctx decl)
|
(fn (ctx decl)
|
||||||
(let ((name (nth decl 1)) (params (nth decl 2))
|
(let ((name (nth decl 1)) (params (nth decl 2))
|
||||||
(results (nth decl 3)) (body (nth decl 4)))
|
(results (nth decl 3)) (body (nth decl 4))
|
||||||
|
(type-params (cond (> (len decl) 5) (nth decl 5) :else nil)))
|
||||||
(let ((fn-ty
|
(let ((fn-ty
|
||||||
(list :ty-func
|
(list :ty-func
|
||||||
(go-decl-params-to-ty-list params) results)))
|
(go-decl-params-to-ty-list params) results)))
|
||||||
@@ -762,10 +770,40 @@
|
|||||||
(= body nil) ctx-with-fn
|
(= body nil) ctx-with-fn
|
||||||
(and (list? body) (= (first body) :block))
|
(and (list? body) (= (first body) :block))
|
||||||
(let ((body-ctx
|
(let ((body-ctx
|
||||||
(go-extend-with-params ctx-with-fn params)))
|
(go-extend-with-type-params
|
||||||
|
(go-extend-with-params ctx-with-fn params)
|
||||||
|
type-params)))
|
||||||
(let ((err
|
(let ((err
|
||||||
(go-check-block body-ctx (nth body 1) results)))
|
(go-check-block body-ctx (nth body 1) results)))
|
||||||
(cond
|
(cond
|
||||||
(go-type-error? err) err
|
(go-type-error? err) err
|
||||||
:else ctx-with-fn)))
|
:else ctx-with-fn)))
|
||||||
:else ctx-with-fn))))))
|
:else ctx-with-fn))))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
go-extend-with-type-params
|
||||||
|
;; Each (:field NAMES CONSTRAINT) field contributes opaque type
|
||||||
|
;; vars: bind each NAME as a type alias to (:ty-param NAME). The
|
||||||
|
;; constraint type is stored alongside so future "constraint
|
||||||
|
;; satisfaction" checks can find it; for v0 it's informational.
|
||||||
|
(fn (ctx type-params)
|
||||||
|
(cond
|
||||||
|
(or (= type-params nil) (= (len type-params) 0)) ctx
|
||||||
|
:else
|
||||||
|
(let ((field (first type-params)))
|
||||||
|
(let ((names (nth field 1)) (constraint (nth field 2)))
|
||||||
|
(go-extend-with-type-params
|
||||||
|
(go-extend-with-type-param-names ctx names constraint)
|
||||||
|
(rest type-params)))))))
|
||||||
|
|
||||||
|
(define
|
||||||
|
go-extend-with-type-param-names
|
||||||
|
(fn (ctx names constraint)
|
||||||
|
(cond
|
||||||
|
(= (len names) 0) ctx
|
||||||
|
:else
|
||||||
|
(let ((nm (first names)))
|
||||||
|
(go-extend-with-type-param-names
|
||||||
|
(go-ctx-extend ctx nm
|
||||||
|
(list :ty-param nm constraint))
|
||||||
|
(rest names) constraint)))))
|
||||||
|
|||||||
@@ -380,14 +380,20 @@ Progress-log line → push `origin/loops/go`.
|
|||||||
- **Acceptance:** eval/ +20 tests — **20/20 cleared.**
|
- **Acceptance:** eval/ +20 tests — **20/20 cleared.**
|
||||||
|
|
||||||
### Phase 7 — Generics (Go 1.18+) ⬜
|
### Phase 7 — Generics (Go 1.18+) ⬜
|
||||||
- Type parameters with constraints (type sets: `interface{ int | float64
|
- [x] **Foundation: parser + typer + eval handle `[T any]` syntax.**
|
||||||
}`, `comparable`, `any`).
|
`gp-parse-type-params` reads `[NAMES CONSTRAINT, ...]` after the
|
||||||
- Type inference at call sites — basic; the full Go inference algorithm
|
func name; AST gets optional 6th slot (legacy 5-slot preserved
|
||||||
is notoriously complex. Implement enough for common cases; document
|
when no `[...]`). Typer binds each name as `(:ty-param NAME
|
||||||
limitations in a Blockers section below.
|
CONSTRAINT)` in the body ctx via `go-extend-with-type-params`.
|
||||||
|
Eval is type-erasing: ignores type info, dispatches by name +
|
||||||
|
arg count. 10 tests: parse (3), types (5), eval (2).
|
||||||
|
- [ ] Type parameters with type-set constraints (`int | float64`,
|
||||||
|
`~int`). Deferred — needs constraint-satisfaction predicate.
|
||||||
|
- [ ] Type inference at call sites — basic. Currently calls must use
|
||||||
|
explicit type args OR rely on type erasure at eval.
|
||||||
- Tests: generic function (`func Map[T, U any](xs []T, f func(T) U) []U`),
|
- Tests: generic function (`func Map[T, U any](xs []T, f func(T) U) []U`),
|
||||||
generic data structure (linked list), constrained type param.
|
generic data structure (linked list), constrained type param.
|
||||||
- **Acceptance:** types/ +30 tests.
|
- **Acceptance:** types/ +30 tests. Currently +5.
|
||||||
|
|
||||||
### Phase 8 — Minimal stdlib (`lib/go/std/`) ⬜
|
### Phase 8 — Minimal stdlib (`lib/go/std/`) ⬜
|
||||||
- Implement just what's needed for representative programs:
|
- Implement just what's needed for representative programs:
|
||||||
@@ -622,6 +628,21 @@ Minimal repro: see `lib/go/lex.sx#gl-oct-digit?` and `#gl-match-op`.
|
|||||||
|
|
||||||
_Newest first. Append one dated entry per commit._
|
_Newest first. Append one dated entry per commit._
|
||||||
|
|
||||||
|
- 2026-05-28 — **Phase 7 foundation: generics syntax wired through
|
||||||
|
parser + typer + eval.** New `gp-parse-type-params` consumes the
|
||||||
|
optional `[NAMES CONSTRAINT, ...]` clause after a func name,
|
||||||
|
reusing `gp-parse-decl-param-group` so the same field shape that
|
||||||
|
recurs in struct fields / var-decls / func params / receivers
|
||||||
|
now also feeds type-parameter lists (6th cross-deliverable use).
|
||||||
|
AST stays backward-compatible: 5 slots when no `[...]` was
|
||||||
|
present, 6 slots when it was. Typer binds each name as
|
||||||
|
`(:ty-param NAME CONSTRAINT)` so body's `(:ty-name "T")`
|
||||||
|
references resolve. Eval ignores type info entirely (type
|
||||||
|
erasure) — generic calls just dispatch by name + arity. 10 new
|
||||||
|
tests (3 parse, 5 types, 2 eval). Total 527/527. **Shape:** the
|
||||||
|
field-binding-group from canonical kit now feeds 6 consumers,
|
||||||
|
validating it as a TRUE cross-deliverable shape (not just a
|
||||||
|
Go-internal artifact). [shapes-static-types-bidirectional]
|
||||||
- 2026-05-27 — **Phase 6 closed (eval 100/100, +20 cleared, total
|
- 2026-05-27 — **Phase 6 closed (eval 100/100, +20 cleared, total
|
||||||
517/517).** Wired panic propagation through `:go` stmt (v0 sync
|
517/517).** Wired panic propagation through `:go` stmt (v0 sync
|
||||||
surfaces the panic back to the spawner — same end-effect as real
|
surfaces the panic back to the spawner — same end-effect as real
|
||||||
|
|||||||
@@ -282,6 +282,44 @@ The kits compose; design accordingly.
|
|||||||
|
|
||||||
_Newest first. Append one dated entry per milestone landed._
|
_Newest first. Append one dated entry per milestone landed._
|
||||||
|
|
||||||
|
- 2026-05-28 — From Go-on-SX Phase 7 foundation — **the field
|
||||||
|
binding-group is a cross-deliverable shape, confirmed by its 6th
|
||||||
|
consumer (type-parameter lists).** Previously documented uses:
|
||||||
|
struct fields, var-decls, const-decls, func params, method
|
||||||
|
receivers. Now type-parameters re-use the EXACT same parser
|
||||||
|
(`gp-parse-decl-param-group`) and the same `(list :field NAMES TY)`
|
||||||
|
shape — only the meaning of TY differs (it's a *constraint* type,
|
||||||
|
not a value type).
|
||||||
|
|
||||||
|
This is the strongest evidence yet that the kit's primary shape
|
||||||
|
should be a generic `binding-group<TyKind>` parametric over the
|
||||||
|
role TY plays. Five roles emerge:
|
||||||
|
|
||||||
|
1. **value-typing** (struct fields, var-decls, params, receivers):
|
||||||
|
TY is the type of values that bind to NAMES.
|
||||||
|
2. **value-pinning** (const-decls): TY is the type of compile-
|
||||||
|
time-known values.
|
||||||
|
3. **constraint-binding** (type-parameter lists): TY is a
|
||||||
|
constraint that the type-variables NAMES must satisfy.
|
||||||
|
4. **kind-binding** (anticipated for higher-kinded types):
|
||||||
|
TY would be a kind that type-constructors NAMES inhabit.
|
||||||
|
5. **trait-binding** (anticipated for Rust-style impl blocks):
|
||||||
|
TY would be the trait the implementations NAMES provide.
|
||||||
|
|
||||||
|
All five share parser + AST shape; they differ in (a) which
|
||||||
|
predicate validates the relationship between NAMES and TY, and
|
||||||
|
(b) what scope NAMES are visible in. The kit should expose a
|
||||||
|
single `parse-binding-group` consumer and let each role plug in
|
||||||
|
its own validator. This is the same lesson the assignable? +
|
||||||
|
constraint-satisfies? pluggable-predicate work surfaced — kit
|
||||||
|
primitives are SHAPES, validators are PLUGINS.
|
||||||
|
|
||||||
|
Concretely: when the kit extracts, the bidirectional checker
|
||||||
|
exposes `extend-ctx-with-binding-group(role, group)` where role
|
||||||
|
selects the validator. Go's type-params bind via role=
|
||||||
|
"constraint-binding"; struct fields bind via "value-typing".
|
||||||
|
Erlang's pattern bindings will bind via something else again.
|
||||||
|
|
||||||
- 2026-05-27 — From Go-on-SX Phase 3 — **interface satisfaction** is the
|
- 2026-05-27 — From Go-on-SX Phase 3 — **interface satisfaction** is the
|
||||||
third pluggable predicate the kit should ship, alongside `assignable?`
|
third pluggable predicate the kit should ship, alongside `assignable?`
|
||||||
and the synth/check skeleton. Go's structural-and-silent
|
and the synth/check skeleton. Go's structural-and-silent
|
||||||
|
|||||||
Reference in New Issue
Block a user