- Reorder load sequence: types→unrestrict→vars→lang→utils→namespaces→core
- Add if-let/when-let/if-some/when-some/let macros and register in core-macro-names
- Add stubs: resolve, update, copy-core-var, copy-var, macrofy, new-var, avoid-method-too-large
- Add dynamic vars: *1, *2, *3, *e, *assert with nil-sentinel
- Fix core-update: use put instead of Janet's update (struct-incompatible)
- Change *clojure-version* from struct to table (mutable for update)
- Fix core-avoid-method-too-large: return @{} not nil
- Fix init-core! nil-sentinel unwrapping
- Fix let* destructuring for {:keys [...]} and sequential patterns
- Fix fn*/defmacro: capture defining ns for symbol resolution in closures
1.2 KiB
Janet apply requires a function/cfunction. When tables/structs are used as lookup maps (like deftype fields, multimethod dispatch tables), they get called via (get f key) not (apply f args). The evaluator's default call path checks (function? f) before apply, falling back to (get f (first args)) for single-arg table/struct calls.
§
Janet structs silently omit entries with nil values: (struct ;[:x nil :y 1]) → {:y 1}. The :x key is completely dropped from the struct, not just set to nil. Use @{} (mutable table) when map needs nil-valued entries. This caused the &env implicit binding to fail — (put new-bindings "&env" {'ns nil}) created a struct that became empty {}, so (:ns &env) failed with unknown method. Fix: use @{} table for bindings that may contain nil values, or use a non-nil sentinel.
§
Janet (put table key nil) silently drops the key — it's a no-op, not a way to store nil. This is SEPARATE from struct-nil-drop: even mutable @{} tables drop nil values on put. The bind-put helper in evaluator.janet stores nil as :jolt/nil sentinel; resolve-sym unwraps it back to nil. All binding put calls in fn*, let*, loop*, macro bodies, and deftype reify MUST use bind-put, not raw put.