Map builder word for SON #151

Closed
opened 2025-12-30 19:14:07 +00:00 by navicore · 2 comments
navicore commented 2025-12-30 19:14:07 +00:00 (Migrated from github.com)

Context

SON (Seq Object Notation) needs a concise way to build maps from key-value pairs:

["name" "Alice" "age" 30 2 kv-map]   # Proposed

Currently building a map requires:

map.make
"name" "Alice" map.set
"age" 30 map.set

Proposed Words

Option A: Count-based

: kv-map ( k1 v1 k2 v2 ... kN vN n -- Map )
  # Takes N pairs and a count, builds map
  ;

"a" 1 "b" 2 2 kv-map   # Map with 2 entries

Option B: Sentinel-based

: { ( -- marker ) ... ;
: } ( marker k1 v1 ... kN vN -- Map )
  # Everything after marker becomes map
  ;

{ "a" 1 "b" 2 }   # Cleaner syntax

Option C: Quotation-based

: kv-map ( quot -- Map )
  # Evaluates quotation, pairs up resulting stack
  ;

[ "a" 1 "b" 2 ] kv-map

Implementation Notes

  • Count-based (Option A) is simplest - just a loop with map.set
  • Could be stdlib, no runtime changes needed
  • Should handle odd counts gracefully (error? ignore last?)

Example Usage in SON

# config.son
: config ( -- Map )
  [
    "host" "localhost"
    "port" 8080
    "debug" true
    3 kv-map
  ] call
;

Relation to SON

This is a quality-of-life feature. Without it, SON map literals are verbose:

# Without kv-map (verbose)
[ map.make "host" "localhost" map.set "port" 8080 map.set ]

# With kv-map (clean)
[ "host" "localhost" "port" 8080 2 kv-map ]

Part of the SON implementation roadmap.
Difficulty: Low
Dependencies: None (can use existing map.make/map.set)

## Context SON (Seq Object Notation) needs a concise way to build maps from key-value pairs: ```seq ["name" "Alice" "age" 30 2 kv-map] # Proposed ``` Currently building a map requires: ```seq map.make "name" "Alice" map.set "age" 30 map.set ``` ## Proposed Words ### Option A: Count-based ```seq : kv-map ( k1 v1 k2 v2 ... kN vN n -- Map ) # Takes N pairs and a count, builds map ; "a" 1 "b" 2 2 kv-map # Map with 2 entries ``` ### Option B: Sentinel-based ```seq : { ( -- marker ) ... ; : } ( marker k1 v1 ... kN vN -- Map ) # Everything after marker becomes map ; { "a" 1 "b" 2 } # Cleaner syntax ``` ### Option C: Quotation-based ```seq : kv-map ( quot -- Map ) # Evaluates quotation, pairs up resulting stack ; [ "a" 1 "b" 2 ] kv-map ``` ## Implementation Notes - Count-based (Option A) is simplest - just a loop with `map.set` - Could be stdlib, no runtime changes needed - Should handle odd counts gracefully (error? ignore last?) ## Example Usage in SON ```seq # config.son : config ( -- Map ) [ "host" "localhost" "port" 8080 "debug" true 3 kv-map ] call ; ``` ## Relation to SON This is a quality-of-life feature. Without it, SON map literals are verbose: ```seq # Without kv-map (verbose) [ map.make "host" "localhost" map.set "port" 8080 map.set ] # With kv-map (clean) [ "host" "localhost" "port" 8080 2 kv-map ] ``` --- Part of the SON implementation roadmap. Difficulty: Low Dependencies: None (can use existing map.make/map.set)
navicore commented 2026-01-04 01:45:18 +00:00 (Migrated from github.com)

Implementation Update

I've implemented a builder pattern approach instead of the count-based kv-map proposed in Option A. Here's why:

The Type System Challenge

Seq's type checker can't express variable-arity stack effects. The proposed:

"a" 1 "b" 2 2 kv-map   # Consumes 4 items + count, produces 1 map

Would require a type signature like ( k1 v1 ... kN vN Int -- Map ) which can't be expressed - the type checker sees ( Int -- Map ) and doesn't know about the pairs.

Solution: Builder Pattern

The new std:map stdlib provides:

include std:map

# Create a map with builder pattern
map-of "host" "localhost" kv "port" 8080 kv "debug" true kv

# Chained style (cleaner for multi-line)
map-of
  "host" "localhost" kv
  "port" 8080 kv
  "debug" true kv

Words provided:

  • map-of - creates empty map (alias for map.make)
  • kv - adds key-value pair (alias for map.set)

Comparison with Original SON Examples

Original goal:

[ "host" "localhost" "port" 8080 2 kv-map ]

Builder pattern:

[ map-of "host" "localhost" kv "port" 8080 kv ] call

The builder approach is slightly more verbose but:

  1. Type-safe (works with Seq's type checker)
  2. No runtime count matching (can't mismatch pair count)
  3. Clear intent at each step

Files Added/Modified

  • crates/compiler/stdlib/map.seq - new stdlib module
  • crates/compiler/src/stdlib_embed.rs - added map to embedded stdlib
  • tests/integration/src/test-kv-map.seq - tests for builder pattern

All 6 tests pass, CI green. Ready for review!

## Implementation Update I've implemented a **builder pattern** approach instead of the count-based `kv-map` proposed in Option A. Here's why: ### The Type System Challenge Seq's type checker can't express variable-arity stack effects. The proposed: ```seq "a" 1 "b" 2 2 kv-map # Consumes 4 items + count, produces 1 map ``` Would require a type signature like `( k1 v1 ... kN vN Int -- Map )` which can't be expressed - the type checker sees `( Int -- Map )` and doesn't know about the pairs. ### Solution: Builder Pattern The new `std:map` stdlib provides: ```seq include std:map # Create a map with builder pattern map-of "host" "localhost" kv "port" 8080 kv "debug" true kv # Chained style (cleaner for multi-line) map-of "host" "localhost" kv "port" 8080 kv "debug" true kv ``` **Words provided:** - `map-of` - creates empty map (alias for `map.make`) - `kv` - adds key-value pair (alias for `map.set`) ### Comparison with Original SON Examples Original goal: ```seq [ "host" "localhost" "port" 8080 2 kv-map ] ``` Builder pattern: ```seq [ map-of "host" "localhost" kv "port" 8080 kv ] call ``` The builder approach is slightly more verbose but: 1. ✅ Type-safe (works with Seq's type checker) 2. ✅ No runtime count matching (can't mismatch pair count) 3. ✅ Clear intent at each step ### Files Added/Modified - `crates/compiler/stdlib/map.seq` - new stdlib module - `crates/compiler/src/stdlib_embed.rs` - added map to embedded stdlib - `tests/integration/src/test-kv-map.seq` - tests for builder pattern All 6 tests pass, CI green. Ready for review!
navicore commented 2026-01-04 01:51:58 +00:00 (Migrated from github.com)
https://github.com/navicore/patch-seq/pull/172
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set.

Reference
navicore/patch-seq#151
No description provided.