Deprecate times, while, and until combinators #273

Closed
opened 2026-01-18 01:55:15 +00:00 by navicore · 1 comment
navicore commented 2026-01-18 01:55:15 +00:00 (Migrated from github.com)

Summary

The times, while, and until combinators all require stack-neutral quotations ([..a -- ..a]). This limitation makes them nearly useless for real-world use cases.

The Problem

Stack-neutral means the quotation must leave the stack in the same shape it found it. What operations are actually stack-neutral?

  • [1 i.+] - increment in place
  • [dup i.*] - square a number
  • [swap] - reorder

What operations are NOT stack-neutral (the 90%+ of real use cases)?

  • [io.write-line] - (String --) - consumes a value
  • [i.+] / [i.*] - (Int Int -- Int) - accumulation
  • [drop] - (T --) - consuming
  • [dup] - (T -- T T) - producing
  • [list.push] - modifies list

The stack-neutral requirement is fundamental to how these combinators work - without knowing iteration count at compile time, the compiler can't allow the stack shape to change per iteration.

Proposed Solution

Deprecate all three combinators and encourage users to use:

  1. Recursion - explicit stack effects in word signatures
  2. List operations - list.fold, list.map, list.each
  3. Tail-recursive helpers - user-defined combinators for specific effects

Migration Path

This is a breaking change. Use semantic versioning to signal this:

  1. Add deprecation warnings in a minor release
  2. Remove the combinators in the next major release
  3. Provide documentation on idiomatic alternatives

Rationale

"The benefits of a clean language trump sugar."

Providing built-ins that only work for ~10% of use cases creates confusion. Users hit the stack-neutral limitation and wonder why their reasonable code doesn't compile. Teaching recursion and list processing as the primary patterns leads to more consistent, understandable code.

  • Issue #271 explored allowing non-stack-neutral quotations with literal counts, but this created inconsistent "sometimes works, sometimes doesn't" behavior

UPDATE: we're completely removing these combinators in 1.1.0 since we found none of the large users of seq, seq-lisp, seq-actor, or seq-lisp-2 use them due to the limitations above.

## Summary The `times`, `while`, and `until` combinators all require **stack-neutral** quotations (`[..a -- ..a]`). This limitation makes them nearly useless for real-world use cases. ## The Problem Stack-neutral means the quotation must leave the stack in the same shape it found it. What operations are actually stack-neutral? - `[1 i.+]` - increment in place - `[dup i.*]` - square a number - `[swap]` - reorder What operations are **NOT** stack-neutral (the 90%+ of real use cases)? - `[io.write-line]` - `(String --)` - consumes a value - `[i.+]` / `[i.*]` - `(Int Int -- Int)` - accumulation - `[drop]` - `(T --)` - consuming - `[dup]` - `(T -- T T)` - producing - `[list.push]` - modifies list The stack-neutral requirement is fundamental to how these combinators work - without knowing iteration count at compile time, the compiler can't allow the stack shape to change per iteration. ## Proposed Solution **Deprecate all three combinators** and encourage users to use: 1. **Recursion** - explicit stack effects in word signatures 2. **List operations** - `list.fold`, `list.map`, `list.each` 3. **Tail-recursive helpers** - user-defined combinators for specific effects ## Migration Path This is a **breaking change**. Use semantic versioning to signal this: 1. Add deprecation warnings in a minor release 2. Remove the combinators in the next major release 3. Provide documentation on idiomatic alternatives ## Rationale > "The benefits of a clean language trump sugar." Providing built-ins that only work for ~10% of use cases creates confusion. Users hit the stack-neutral limitation and wonder why their reasonable code doesn't compile. Teaching recursion and list processing as the primary patterns leads to more consistent, understandable code. ## Related - Issue #271 explored allowing non-stack-neutral quotations with literal counts, but this created inconsistent "sometimes works, sometimes doesn't" behavior --- UPDATE: we're completely removing these combinators in 1.1.0 since we found none of the large users of seq, seq-lisp, seq-actor, or seq-lisp-2 use them due to the limitations above.
navicore commented 2026-01-19 17:47:50 +00:00 (Migrated from github.com)
https://github.com/navicore/patch-seq/pull/281
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#273
No description provided.