times: allow non-stack-neutral quotations when count is a literal #271

Closed
opened 2026-01-17 17:47:43 +00:00 by navicore · 1 comment
navicore commented 2026-01-17 17:47:43 +00:00 (Migrated from github.com)

Summary

The times combinator currently requires stack-neutral quotations [..a -- ..a], which significantly limits its usefulness for common patterns.

Current Behavior

seq> 1 2 3 4 5 [i.*] 2 times
times: stack type mismatch. Expected (..a$1 [..a$1 -- ..a$1] Int), got ... 

The quotation [i.*] has effect [Int Int -- Int] (consumes 2, produces 1), which isn't stack-neutral, so it's rejected.

Expected Behavior

When the count is a compile-time literal, the compiler should type-check the unrolled form:

[i.*] 2 times  →  [i.*] call [i.*] call  →  effect: [Int Int Int -- Int]

This is perfectly typeable. The restriction to [..a -- ..a] is only necessary when the count is a runtime value (where the final stack shape can't be statically determined).

Use Cases That Don't Work Today

# Multiply top N stack values together
1 2 3 4 5 [i.*] 4 times   # should produce 120

# Apply transformation N times with different arity
[drop] 3 times   # drop 3 values

Use Cases That Do Work

# Stack-neutral operations
1 [dup i.+] 3 times   # 1 → 2 → 4 → 8

# Side-effect loops (stack-neutral by nature)
[ "hello" io.write-line ] 5 times

Proposed Solution

In the typechecker, when times has a literal count:

  1. Don't require [..a -- ..a]
  2. Compute the composed effect of applying the quotation N times
  3. Use that as the overall effect

For runtime counts, keep the current [..a -- ..a] requirement since the stack effect genuinely can't be determined.

The same logic might apply to while and until with statically-known iteration bounds, though those are less common.

## Summary The `times` combinator currently requires stack-neutral quotations `[..a -- ..a]`, which significantly limits its usefulness for common patterns. ## Current Behavior ```seq seq> 1 2 3 4 5 [i.*] 2 times times: stack type mismatch. Expected (..a$1 [..a$1 -- ..a$1] Int), got ... ``` The quotation `[i.*]` has effect `[Int Int -- Int]` (consumes 2, produces 1), which isn't stack-neutral, so it's rejected. ## Expected Behavior When the count is a **compile-time literal**, the compiler should type-check the unrolled form: ```seq [i.*] 2 times → [i.*] call [i.*] call → effect: [Int Int Int -- Int] ``` This is perfectly typeable. The restriction to `[..a -- ..a]` is only necessary when the count is a runtime value (where the final stack shape can't be statically determined). ## Use Cases That Don't Work Today ```seq # Multiply top N stack values together 1 2 3 4 5 [i.*] 4 times # should produce 120 # Apply transformation N times with different arity [drop] 3 times # drop 3 values ``` ## Use Cases That Do Work ```seq # Stack-neutral operations 1 [dup i.+] 3 times # 1 → 2 → 4 → 8 # Side-effect loops (stack-neutral by nature) [ "hello" io.write-line ] 5 times ``` ## Proposed Solution In the typechecker, when `times` has a literal count: 1. Don't require `[..a -- ..a]` 2. Compute the composed effect of applying the quotation N times 3. Use that as the overall effect For runtime counts, keep the current `[..a -- ..a]` requirement since the stack effect genuinely can't be determined. ## Related The same logic might apply to `while` and `until` with statically-known iteration bounds, though those are less common.
navicore commented 2026-01-18 01:28:12 +00:00 (Migrated from github.com)
https://github.com/navicore/patch-seq/pull/272
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#271
No description provided.