I.5
Evaluation and bindings
Evaluation order, lexical scope, immutable bindings, and recursion.
Scope
This chapter covers evaluation order, lexical scope, and immutable bindings. It also covers duplicate definitions, recursive procedures, and tail calls.
Evaluation order
Evaluation computes values by following the program. A first failure ends the current evaluation path unless a permitted guard handles it. Each later expression stays unevaluated until the preceding expression has completed.
The cases use symbolic finite limits large enough to reach the stated language result. Resource event order is defined in Resources.
5.1 Evaluate the operator and arguments in order
An application evaluates its operator first. It then evaluates every argument from left to right. After those evaluations complete, it checks that the operator is a procedure and that the argument count is accepted. The called operation checks its argument domains in the specified order. The first failure takes precedence over every later check.
Why
Pure expressions can still fail. A fixed order lets every product report the same first failure for the same request.
An operator failure comes first
lispex.example.v2.evaluation.operator-before-arguments · rev. 2
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
((car '()) (cdr '()))))
)
Request and files
{
"notation": "documentation-request",
"source": "source.scm",
"semantics": {
"language": "Lispex 2.0",
"standardLibrary": "one fixed library identity",
"unicode": "one fixed Unicode semantics version",
"resourceModel": "one fixed finite resource model"
},
"limits": {
"prepare": "finite values sufficient to finish preparation checks",
"evaluate": "finite values sufficient for the stated finite calculation"
},
"requests": [
{
"input": {
"kind": "integer",
"value": "0"
}
}
],
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "source.scm"
}
]
}
domain.typeOutcome details
{
"kind": "language-failure",
"valueCount": 0,
"condition": {
"phase": "evaluate",
"code": "domain.type",
"data": {
"operation": "car",
"argument": "0",
"path": [],
"expected": "pair",
"actual": "empty-list"
}
}
}Specification outcome. Product runs are pending the 2.0 adapter.
Product invocation
Expected result
The operator produces the car domain failure before the argument is evaluated. Product invocation awaits its 2.0 adapter.
The cdr expression would also fail. The plan requires the car failure and no result datum.
The left argument fails first
lispex.example.v2.evaluation.arguments-left-to-right · rev. 2
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(+ (car '()) (cdr '()))))
)
Request and files
{
"notation": "documentation-request",
"source": "source.scm",
"semantics": {
"language": "Lispex 2.0",
"standardLibrary": "one fixed library identity",
"unicode": "one fixed Unicode semantics version",
"resourceModel": "one fixed finite resource model"
},
"limits": {
"prepare": "finite values sufficient to finish preparation checks",
"evaluate": "finite values sufficient for the stated finite calculation"
},
"requests": [
{
"input": {
"kind": "integer",
"value": "0"
}
}
],
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "source.scm"
}
]
}
domain.typeOutcome details
{
"kind": "language-failure",
"valueCount": 0,
"condition": {
"phase": "evaluate",
"code": "domain.type",
"data": {
"operation": "car",
"argument": "0",
"path": [],
"expected": "pair",
"actual": "empty-list"
}
}
}Specification outcome. Product runs are pending the 2.0 adapter.
Product invocation
Expected result
The left argument produces the car domain failure before the right argument is evaluated. Product invocation awaits its 2.0 adapter.
Neither the second argument nor the addition can replace the first failure.
An argument failure precedes callability
lispex.example.v2.evaluation.arguments-before-call-check · rev. 2
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(0 (car '()))))
)
Request and files
{
"notation": "documentation-request",
"source": "source.scm",
"semantics": {
"language": "Lispex 2.0",
"standardLibrary": "one fixed library identity",
"unicode": "one fixed Unicode semantics version",
"resourceModel": "one fixed finite resource model"
},
"limits": {
"prepare": "finite values sufficient to finish preparation checks",
"evaluate": "finite values sufficient for the stated finite calculation"
},
"requests": [
{
"input": {
"kind": "integer",
"value": "0"
}
}
],
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "source.scm"
}
]
}
domain.typeOutcome details
{
"kind": "language-failure",
"valueCount": 0,
"condition": {
"phase": "evaluate",
"code": "domain.type",
"data": {
"operation": "car",
"argument": "0",
"path": [],
"expected": "pair",
"actual": "empty-list"
}
}
}Specification outcome. Product runs are pending the 2.0 adapter.
Product invocation
Expected result
The argument produces the car domain failure before the operator is checked for callability. Product invocation awaits its 2.0 adapter.
The integer operator cannot be called. Its later callability failure does not replace the argument failure.
An argument failure precedes arity
lispex.example.v2.evaluation.arguments-before-arity · rev. 2
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
((lambda (x) x) 1 (car '()))))
)
Request and files
{
"notation": "documentation-request",
"source": "source.scm",
"semantics": {
"language": "Lispex 2.0",
"standardLibrary": "one fixed library identity",
"unicode": "one fixed Unicode semantics version",
"resourceModel": "one fixed finite resource model"
},
"limits": {
"prepare": "finite values sufficient to finish preparation checks",
"evaluate": "finite values sufficient for the stated finite calculation"
},
"requests": [
{
"input": {
"kind": "integer",
"value": "0"
}
}
],
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "source.scm"
}
]
}
domain.typeOutcome details
{
"kind": "language-failure",
"valueCount": 0,
"condition": {
"phase": "evaluate",
"code": "domain.type",
"data": {
"operation": "car",
"argument": "0",
"path": [],
"expected": "pair",
"actual": "empty-list"
}
}
}Specification outcome. Product runs are pending the 2.0 adapter.
Product invocation
Expected result
The second argument produces the car domain failure before the argument count is checked. Product invocation awaits its 2.0 adapter.
The procedure accepts one argument. The plan requires the earlier domain failure instead of an arity failure.
5.2 Evaluate only the selected expressions
An if form has one test and two written branches. A cond form ends with one nonempty else clause. The conditional grammar fixes these source forms. A conditional evaluates its test before the selected branch. Only #f is false in a test. Every other value is true, including the empty list and exact zero. A conditional does not evaluate an unselected branch. The selected branch forwards its complete internal result sequence. An author may explicitly return zero values with values. A missing branch never supplies an implicit result. The and and or forms evaluate operands from left to right and stop when the result is determined. A begin body evaluates its expressions in order and returns the values of its final expression.
Why
Selecting a branch must also select which failures and resource events can occur. Evaluating unused expressions would change the meaning of the rule.
Only #f is false.
Written alternatives make the result policy explicit for every conditional path. They preserve internal multiple values without inventing an unmatched result.
Cond tests run once in source order. Each test requires one value. Only #f selects the next clause. A selected test-only clause returns that test value. A selected body returns the values of its final expression. An arrow clause evaluates its recipient once and calls it with the saved test value. The recipient requires one procedure value and follows the ordinary call checks. No later clause runs after a match or a failure. The final else body runs when every test is false. Selected branches may explicitly return zero, one, or many internal values. The single-value context rule and entry result rule still apply at their boundaries.
The else and arrow markers use the binding-independent clause interpretation from Conditions. Preparation rejects malformed conditional forms under the existing source order. The reached form is checked before its child expressions. Cond clause shape checks follow source order. The final else requirement is checked after those clause shape checks. A reservation failure keeps its existing precedence.
| Malformed form | Phase | Code | Data |
|---|---|---|---|
| If without exactly three operands | normalize | syntax.form | form = if and reason = expected-shape |
| Cond without a final else clause | normalize | syntax.form | form = cond and reason = expected-shape |
| Empty or improper cond clause | normalize | syntax.form | form = cond and reason = clause-shape |
| Else before another cond clause | normalize | syntax.form | form = cond and reason = else-position |
| Cond else without an expression | normalize | syntax.form | form = cond and reason = else-body |
| Cond arrow without exactly one recipient | normalize | syntax.form | form = cond and reason = arrow-shape |
These are existing fields and reasons from the diagnostic catalog. An empty cond has no final else and fails with expected-shape. Neither source normalization nor prepared validation synthesizes an alternative. The resource event catalog supplies the existing conditional schedule.
Require written conditional alternatives
lispex.example.v2.evaluation.conditional-forms-rejected · rev. 1
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(if #t 1))))
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(if #f 1))))
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(if #t 1 2 3))))
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(cond (#t 1)))))
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(cond (#f 1)))))
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(cond))))
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(cond () (else 7)))))
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(cond (else 1) (#t 2)))))
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(cond (#f 1) (else)))))
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(cond (#t => (lambda (x) x) 9) (else 0)))))
Request and files
{
"notation": "documentation-request",
"source": "source.scm",
"semantics": {
"language": "Lispex 2.0",
"standardLibrary": "one fixed library identity",
"unicode": "one fixed Unicode semantics version",
"resourceModel": "one fixed finite resource model"
},
"limits": {
"prepare": "finite values sufficient to finish preparation checks",
"evaluate": "finite values sufficient for the stated finite calculation"
},
"requests": [
{
"case": "if-true-missing",
"source": "source.scm",
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "source.scm"
}
],
"input": {
"kind": "integer",
"value": "0"
}
},
{
"case": "if-false-missing",
"source": "if-false-missing.scm",
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "if-false-missing.scm"
}
],
"input": {
"kind": "integer",
"value": "0"
}
},
{
"case": "if-extra",
"source": "if-extra.scm",
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "if-extra.scm"
}
],
"input": {
"kind": "integer",
"value": "0"
}
},
{
"case": "cond-true-missing",
"source": "cond-true-missing.scm",
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "cond-true-missing.scm"
}
],
"input": {
"kind": "integer",
"value": "0"
}
},
{
"case": "cond-false-missing",
"source": "cond-false-missing.scm",
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "cond-false-missing.scm"
}
],
"input": {
"kind": "integer",
"value": "0"
}
},
{
"case": "cond-empty",
"source": "cond-empty.scm",
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "cond-empty.scm"
}
],
"input": {
"kind": "integer",
"value": "0"
}
},
{
"case": "cond-empty-clause",
"source": "cond-empty-clause.scm",
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "cond-empty-clause.scm"
}
],
"input": {
"kind": "integer",
"value": "0"
}
},
{
"case": "cond-early-else",
"source": "cond-early-else.scm",
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "cond-early-else.scm"
}
],
"input": {
"kind": "integer",
"value": "0"
}
},
{
"case": "cond-empty-else",
"source": "cond-empty-else.scm",
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "cond-empty-else.scm"
}
],
"input": {
"kind": "integer",
"value": "0"
}
},
{
"case": "cond-arrow-shape",
"source": "cond-arrow-shape.scm",
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "cond-arrow-shape.scm"
}
],
"input": {
"kind": "integer",
"value": "0"
}
}
],
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "source.scm"
}
]
}
syntax.formOutcome details
{
"case": "if-true-missing",
"kind": "prepare-failure",
"valueCount": 0,
"diagnostic": {
"phase": "normalize",
"code": "syntax.form",
"data": {
"form": "if",
"reason": "expected-shape"
}
}
}syntax.formOutcome details
{
"case": "if-false-missing",
"kind": "prepare-failure",
"valueCount": 0,
"diagnostic": {
"phase": "normalize",
"code": "syntax.form",
"data": {
"form": "if",
"reason": "expected-shape"
}
}
}syntax.formOutcome details
{
"case": "if-extra",
"kind": "prepare-failure",
"valueCount": 0,
"diagnostic": {
"phase": "normalize",
"code": "syntax.form",
"data": {
"form": "if",
"reason": "expected-shape"
}
}
}syntax.formOutcome details
{
"case": "cond-true-missing",
"kind": "prepare-failure",
"valueCount": 0,
"diagnostic": {
"phase": "normalize",
"code": "syntax.form",
"data": {
"form": "cond",
"reason": "expected-shape"
}
}
}syntax.formOutcome details
{
"case": "cond-false-missing",
"kind": "prepare-failure",
"valueCount": 0,
"diagnostic": {
"phase": "normalize",
"code": "syntax.form",
"data": {
"form": "cond",
"reason": "expected-shape"
}
}
}syntax.formOutcome details
{
"case": "cond-empty",
"kind": "prepare-failure",
"valueCount": 0,
"diagnostic": {
"phase": "normalize",
"code": "syntax.form",
"data": {
"form": "cond",
"reason": "expected-shape"
}
}
}syntax.formOutcome details
{
"case": "cond-empty-clause",
"kind": "prepare-failure",
"valueCount": 0,
"diagnostic": {
"phase": "normalize",
"code": "syntax.form",
"data": {
"form": "cond",
"reason": "clause-shape"
}
}
}syntax.formOutcome details
{
"case": "cond-early-else",
"kind": "prepare-failure",
"valueCount": 0,
"diagnostic": {
"phase": "normalize",
"code": "syntax.form",
"data": {
"form": "cond",
"reason": "else-position"
}
}
}syntax.formOutcome details
{
"case": "cond-empty-else",
"kind": "prepare-failure",
"valueCount": 0,
"diagnostic": {
"phase": "normalize",
"code": "syntax.form",
"data": {
"form": "cond",
"reason": "else-body"
}
}
}syntax.formOutcome details
{
"case": "cond-arrow-shape",
"kind": "prepare-failure",
"valueCount": 0,
"diagnostic": {
"phase": "normalize",
"code": "syntax.form",
"data": {
"form": "cond",
"reason": "arrow-shape"
}
}
}Specification outcome. Product runs are pending the 2.0 adapter.
Semantic details
{
"entryEvaluated": false,
"canonicalOutcomeProduced": false,
"evaluationReceiptEligible": false
}Product invocation
Expected result
Preparation rejects each malformed conditional before evaluation. Product invocation awaits its 2.0 adapter.
A true test cannot excuse a missing written alternative. Each source is an independent closed module.
Select each admitted clause shape
lispex.example.v2.evaluation.conditional-selected-results · rev. 1
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(list
(if #f (car '()) input)
(cond (#f (car '())) (input) (else (car '())))
(cond (#f (car '())) (input => (lambda (value) (+ value 1))) (else (car '())))
(cond (#f (car '())) (else 11 13))
(cond (else 17))))))
Request and files
{
"notation": "documentation-request",
"source": "source.scm",
"semantics": {
"language": "Lispex 2.0",
"standardLibrary": "one fixed library identity",
"unicode": "one fixed Unicode semantics version",
"resourceModel": "one fixed finite resource model"
},
"limits": {
"prepare": "finite values sufficient to finish preparation checks",
"evaluate": "finite values sufficient for the stated finite calculation"
},
"requests": [
{
"input": {
"kind": "integer",
"value": "7"
}
}
],
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "source.scm"
}
]
}
(7 7 8 13 17)Outcome details
{
"kind": "value",
"valueCount": 1,
"datum": {
"kind": "list",
"items": [
{
"kind": "integer",
"value": "7"
},
{
"kind": "integer",
"value": "7"
},
{
"kind": "integer",
"value": "8"
},
{
"kind": "integer",
"value": "13"
},
{
"kind": "integer",
"value": "17"
}
]
}
}Specification outcome. Product runs are pending the 2.0 adapter.
Semantic details
{
"entryEvaluated": true,
"canonicalOutcomeProduced": true,
"evaluationReceiptEligible": true
}Product invocation
Expected result
The written branches return the list with elements 7, 7, 8, 13, and 17. Product invocation awaits its 2.0 adapter.
Unselected failures remain unevaluated. Test-only and recipient clauses keep the saved test value.
Forward explicitly returned internal values
lispex.example.v2.evaluation.conditional-explicit-values · rev. 1
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(list
(call-with-values
(lambda () (if #f 99 (values)))
(lambda results (length results)))
(call-with-values
(lambda () (cond (#f 99) (else (values))))
(lambda results (length results)))
(call-with-values
(lambda () (if #t (values 2 3) (car '())))
(lambda results results))
(call-with-values
(lambda () (cond (#t (values 5 8)) (else (car '()))))
(lambda results results))))))
Request and files
{
"notation": "documentation-request",
"source": "source.scm",
"semantics": {
"language": "Lispex 2.0",
"standardLibrary": "one fixed library identity",
"unicode": "one fixed Unicode semantics version",
"resourceModel": "one fixed finite resource model"
},
"limits": {
"prepare": "finite values sufficient to finish preparation checks",
"evaluate": "finite values sufficient for the stated finite calculation"
},
"requests": [
{
"input": {
"kind": "integer",
"value": "0"
}
}
],
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "source.scm"
}
]
}
(0 0 (2 3) (5 8))Outcome details
{
"kind": "value",
"valueCount": 1,
"datum": {
"kind": "list",
"items": [
{
"kind": "integer",
"value": "0"
},
{
"kind": "integer",
"value": "0"
},
{
"kind": "list",
"items": [
{
"kind": "integer",
"value": "2"
},
{
"kind": "integer",
"value": "3"
}
]
},
{
"kind": "list",
"items": [
{
"kind": "integer",
"value": "5"
},
{
"kind": "integer",
"value": "8"
}
]
}
]
}
}Specification outcome. Product runs are pending the 2.0 adapter.
Semantic details
{
"entryEvaluated": true,
"canonicalOutcomeProduced": true,
"evaluationReceiptEligible": true
}Product invocation
Expected result
Written branches preserve zero and multiple values for their explicit receivers. Product invocation awaits its 2.0 adapter.
The first two receivers observe zero values. The later receivers collect two values each. The entry returns one list Datum.
Leave the other branch unevaluated
lispex.example.v2.evaluation.unselected-branch · rev. 2
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(if #t 7 (car '()))))
)
Request and files
{
"notation": "documentation-request",
"source": "source.scm",
"semantics": {
"language": "Lispex 2.0",
"standardLibrary": "one fixed library identity",
"unicode": "one fixed Unicode semantics version",
"resourceModel": "one fixed finite resource model"
},
"limits": {
"prepare": "finite values sufficient to finish preparation checks",
"evaluate": "finite values sufficient for the stated finite calculation"
},
"requests": [
{
"input": {
"kind": "integer",
"value": "0"
}
}
],
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "source.scm"
}
]
}
7Outcome details
{
"kind": "value",
"valueCount": 1,
"datum": {
"kind": "integer",
"value": "7"
}
}Specification outcome. Product runs are pending the 2.0 adapter.
Product invocation
Expected result
The selected branch returns the exact integer 7 and the other branch is not evaluated. Product invocation awaits its 2.0 adapter.
The unused car expression does not cause a failure or consume evaluation work.
Stop after the deciding operand
lispex.example.v2.evaluation.short-circuit · rev. 2
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(list (and #f (car '()))
(or 9 (cdr '())))))
)
Request and files
{
"notation": "documentation-request",
"source": "source.scm",
"semantics": {
"language": "Lispex 2.0",
"standardLibrary": "one fixed library identity",
"unicode": "one fixed Unicode semantics version",
"resourceModel": "one fixed finite resource model"
},
"limits": {
"prepare": "finite values sufficient to finish preparation checks",
"evaluate": "finite values sufficient for the stated finite calculation"
},
"requests": [
{
"input": {
"kind": "integer",
"value": "0"
}
}
],
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "source.scm"
}
]
}
(#f 9)Outcome details
{
"kind": "value",
"valueCount": 1,
"datum": {
"kind": "list",
"items": [
{
"kind": "boolean",
"value": false
},
{
"kind": "integer",
"value": "9"
}
]
}
}Specification outcome. Product runs are pending the 2.0 adapter.
Product invocation
Expected result
The rule returns the list containing false and the exact integer 9. Product invocation awaits its 2.0 adapter.
Both failing expressions are skipped after the preceding operand determines the result.
A sequence stops at its first failure
lispex.example.v2.evaluation.sequence-first-failure · rev. 3
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(begin
1
(car '())
(cdr '()))))
)
Request and files
{
"notation": "documentation-request",
"source": "source.scm",
"semantics": {
"language": "Lispex 2.0",
"standardLibrary": "one fixed library identity",
"unicode": "one fixed Unicode semantics version",
"resourceModel": "one fixed finite resource model"
},
"limits": {
"prepare": "finite values sufficient to finish preparation checks",
"evaluate": "finite values sufficient for the stated finite calculation"
},
"requests": [
{
"input": {
"kind": "integer",
"value": "0"
}
}
],
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "source.scm"
}
]
}
domain.typeOutcome details
{
"kind": "language-failure",
"valueCount": 0,
"condition": {
"phase": "evaluate",
"code": "domain.type",
"data": {
"operation": "car",
"argument": "0",
"path": [],
"expected": "pair",
"actual": "empty-list"
}
}
}Specification outcome. Product runs are pending the 2.0 adapter.
Product invocation
Expected result
The sequence stops at the car domain failure and returns no datum. Product invocation awaits its 2.0 adapter.
The cdr expression is later in the sequence and cannot replace the first failure.
Treat only false as false
lispex.example.v2.evaluation.truth-tests · rev. 2
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(list (if #f 1 2)
(if '() 3 4)
(if 0 5 6))))
)
Request and files
{
"notation": "documentation-request",
"source": "source.scm",
"semantics": {
"language": "Lispex 2.0",
"standardLibrary": "one fixed library identity",
"unicode": "one fixed Unicode semantics version",
"resourceModel": "one fixed finite resource model"
},
"limits": {
"prepare": "finite values sufficient to finish preparation checks",
"evaluate": "finite values sufficient for the stated finite calculation"
},
"requests": [
{
"input": {
"kind": "integer",
"value": "0"
}
}
],
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "source.scm"
}
]
}
(2 3 5)Outcome details
{
"kind": "value",
"valueCount": 1,
"datum": {
"kind": "list",
"items": [
{
"kind": "integer",
"value": "2"
},
{
"kind": "integer",
"value": "3"
},
{
"kind": "integer",
"value": "5"
}
]
}
}Specification outcome. Product runs are pending the 2.0 adapter.
Product invocation
Expected result
The rule returns the list of exact integers 2, 3, and 5. Product invocation awaits its 2.0 adapter.
The empty list and exact zero both select the true branch. Only #f selects the false branch.
Conditional grammar
Conditional = IfForm | CondForm ;
IfForm = "(" "if" expression expression expression ")" ;
CondForm = "(" "cond" clause { clause } ")" ;
The imported expression is a reader datum admitted as an expression. The imported clause uses the guard grammar. A clause has a test and body, a test alone, a test and recipient, or an else body. The cond form requires the else clause exactly once and last. Its body must be nonempty. Every preceding clause is a test clause. The if form requires both branches. Keywords compare decoded identifier values under the reader grammar. These restrictions concern ordinary conditionals. Guard keeps its own optional else and unmatched propagation rule.
Scope and bindings
A binding associates a name with one value. Its scope is the region of source where that association is visible. Lexical scope follows the written nesting of bindings.
A procedure retains access to the bindings visible where it is defined. A call introduces fresh argument bindings. Procedures defines parameter counts and returned values.
5.3 Resolve a name in its lexical scope
A name refers to the nearest enclosing lexical binding with that name. A nested scope may introduce a new binding with the same spelling. A procedure uses its captured lexical bindings when it is called. A caller cannot change those bindings by declaring a name in its own scope.
Why
A procedure can be understood from its definition and captured values. Calling it from another scope preserves that meaning.
A procedure keeps the enclosing value
lispex.example.v2.evaluation.lexical-capture · rev. 1
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(let ((offset 5))
(let ((add-offset (lambda (x) (+ x offset))))
(let ((offset 99))
(add-offset input))))))
)
Request and files
{
"notation": "documentation-request",
"source": "source.scm",
"semantics": {
"language": "Lispex 2.0",
"standardLibrary": "one fixed library identity",
"unicode": "one fixed Unicode semantics version",
"resourceModel": "one fixed finite resource model"
},
"limits": {
"prepare": "finite values sufficient to finish preparation checks",
"evaluate": "finite values sufficient for the stated finite calculation"
},
"requests": [
{
"input": {
"kind": "integer",
"value": "7"
}
}
],
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "source.scm"
}
]
}
12Outcome details
{
"kind": "value",
"valueCount": 1,
"datum": {
"kind": "integer",
"value": "12"
}
}Specification outcome. Product runs are pending the 2.0 adapter.
Product invocation
Expected result
The procedure uses the captured offset 5 and returns the exact integer 12. Product invocation awaits its 2.0 adapter.
The inner offset 99 is a separate binding. Creating it leaves the captured binding unchanged.
5.4 Distinguish parallel and sequential bindings
The let form evaluates its initializers from left to right in the enclosing scope. It introduces the new bindings for the body after all initializers succeed. The let form evaluates each initializer with the preceding new bindings in scope. Each let binding introduces a nested scope. A later binding with the same name shadows the earlier binding. Each initializer follows the single-value context rule.
Why
Explicit initializer visibility prevents a name from acquiring a different value because of evaluation order or an implementation environment layout.
Each let* binding introduces a nested scope.
A let initializer sees the enclosing name
lispex.example.v2.evaluation.parallel-bindings · rev. 2
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(let ((x 40))
(let ((x 1)
(y x))
(+ x y)))))
)
Request and files
{
"notation": "documentation-request",
"source": "source.scm",
"semantics": {
"language": "Lispex 2.0",
"standardLibrary": "one fixed library identity",
"unicode": "one fixed Unicode semantics version",
"resourceModel": "one fixed finite resource model"
},
"limits": {
"prepare": "finite values sufficient to finish preparation checks",
"evaluate": "finite values sufficient for the stated finite calculation"
},
"requests": [
{
"input": {
"kind": "integer",
"value": "0"
}
}
],
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "source.scm"
}
]
}
41Outcome details
{
"kind": "value",
"valueCount": 1,
"datum": {
"kind": "integer",
"value": "41"
}
}Specification outcome. Product runs are pending the 2.0 adapter.
Product invocation
Expected result
The initializer for y sees the enclosing x and the rule returns the exact integer 41. Product invocation awaits its 2.0 adapter.
The new x is visible in the body. It is not visible to another initializer in the same let.
A let* initializer sees the preceding name
lispex.example.v2.evaluation.sequential-bindings · rev. 2
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(let ((x 40))
(let* ((x 1)
(y x))
(+ x y)))))
)
Request and files
{
"notation": "documentation-request",
"source": "source.scm",
"semantics": {
"language": "Lispex 2.0",
"standardLibrary": "one fixed library identity",
"unicode": "one fixed Unicode semantics version",
"resourceModel": "one fixed finite resource model"
},
"limits": {
"prepare": "finite values sufficient to finish preparation checks",
"evaluate": "finite values sufficient for the stated finite calculation"
},
"requests": [
{
"input": {
"kind": "integer",
"value": "0"
}
}
],
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "source.scm"
}
]
}
2Outcome details
{
"kind": "value",
"valueCount": 1,
"datum": {
"kind": "integer",
"value": "2"
}
}Specification outcome. Product runs are pending the 2.0 adapter.
Product invocation
Expected result
The initializer for y sees the preceding x and the rule returns the exact integer 2. Product invocation awaits its 2.0 adapter.
The two binding forms have distinct scope rules even though both evaluate initializers from left to right.
Reuse a name in a later sequential scope
lispex.example.v2.evaluation.sequential-shadowing · rev. 2
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(let* ((x 1)
(x (+ x 1)))
x)))
)
Request and files
{
"notation": "documentation-request",
"source": "source.scm",
"semantics": {
"language": "Lispex 2.0",
"standardLibrary": "one fixed library identity",
"unicode": "one fixed Unicode semantics version",
"resourceModel": "one fixed finite resource model"
},
"limits": {
"prepare": "finite values sufficient to finish preparation checks",
"evaluate": "finite values sufficient for the stated finite calculation"
},
"requests": [
{
"input": {
"kind": "integer",
"value": "0"
}
}
],
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "source.scm"
}
]
}
2Outcome details
{
"kind": "value",
"valueCount": 1,
"datum": {
"kind": "integer",
"value": "2"
}
}Specification outcome. Product runs are pending the 2.0 adapter.
Product invocation
Expected result
The later initializer reads the earlier x and the body returns the exact integer 2. Product invocation awaits its 2.0 adapter.
Each let* binding belongs to a nested scope. The earlier binding is never updated.
5.5 Keep each binding unchanged
A binding keeps its value for its lifetime. The language does not provide set! to replace that value. Passing a value to another procedure or capturing it in a closure does not grant an operation that changes the binding.
Why
A rule has no hidden history of assignments. Its lexical bindings remain valid inputs to reasoning, replay, and the meaning graph.
Reject an assignment
lispex.example.v2.evaluation.assignment-rejected · rev. 3
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(set! input 1)))
)
Request and files
{
"notation": "documentation-request",
"source": "source.scm",
"semantics": {
"language": "Lispex 2.0",
"standardLibrary": "one fixed library identity",
"unicode": "one fixed Unicode semantics version",
"resourceModel": "one fixed finite resource model"
},
"limits": {
"prepare": "finite values sufficient to finish preparation checks",
"evaluate": "finite values sufficient for the stated finite calculation"
},
"requests": [
{
"input": {
"kind": "integer",
"value": "9"
}
}
],
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "source.scm"
}
]
}
binding.immutableOutcome details
{
"kind": "failure",
"valueCount": 0,
"diagnostic": {
"phase": "normalize",
"code": "binding.immutable",
"data": {
"name": "input"
}
}
}Specification outcome. Product runs are pending the 2.0 adapter.
Semantic details
{
"canonicalOutcomeProduced": false,
"evaluationReceiptEligible": false
}Product invocation
Expected result
The request is rejected and does not return a datum. Product invocation awaits its 2.0 adapter.
This is a new rejection expectation for 2.0. No successful assignment behavior is carried from the earlier pilot.
Duplicate definitions
5.6 Reject a repeated definition in one scope
A definition introduces a new binding in its definition scope. Defining the same name again in that scope is a static error. Repeated names in one parallel binding group are also rejected. An enclosing binding and a nested binding remain separate definitions.
Why
Replacing a definition would change the value seen by existing references. Rejecting duplicates keeps each name association immutable.
Reject a second definition
lispex.example.v2.evaluation.redefinition-rejected · rev. 3
(module main
(import)
(export rule)
(entry rule)
(define threshold 5)
(define threshold 6)
(define rule
(lambda (input)
threshold))
)
Request and files
{
"notation": "documentation-request",
"source": "source.scm",
"semantics": {
"language": "Lispex 2.0",
"standardLibrary": "one fixed library identity",
"unicode": "one fixed Unicode semantics version",
"resourceModel": "one fixed finite resource model"
},
"limits": {
"prepare": "finite values sufficient to finish preparation checks",
"evaluate": "finite values sufficient for the stated finite calculation"
},
"requests": [
{
"input": {
"kind": "integer",
"value": "0"
}
}
],
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "source.scm"
}
]
}
binding.duplicateOutcome details
{
"kind": "prepare-failure",
"valueCount": 0,
"diagnostic": {
"phase": "normalize",
"code": "binding.duplicate",
"data": {
"name": "threshold",
"scope": "module"
}
}
}Specification outcome. Product runs are pending the 2.0 adapter.
Semantic details
{
"canonicalOutcomeProduced": false,
"evaluationReceiptEligible": false
}Product invocation
Expected result
Preparation rejects the duplicate threshold definition and evaluation does not begin. Product invocation awaits its 2.0 adapter.
The earlier value is not updated and the later value is not selected.
Reject duplicate names in one let
lispex.example.v2.evaluation.parallel-duplicate-rejected · rev. 3
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(let ((x 1)
(x 2))
x)))
)
Request and files
{
"notation": "documentation-request",
"source": "source.scm",
"semantics": {
"language": "Lispex 2.0",
"standardLibrary": "one fixed library identity",
"unicode": "one fixed Unicode semantics version",
"resourceModel": "one fixed finite resource model"
},
"limits": {
"prepare": "finite values sufficient to finish preparation checks",
"evaluate": "finite values sufficient for the stated finite calculation"
},
"requests": [
{
"input": {
"kind": "integer",
"value": "0"
}
}
],
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "source.scm"
}
]
}
binding.duplicateOutcome details
{
"kind": "prepare-failure",
"valueCount": 0,
"diagnostic": {
"phase": "normalize",
"code": "binding.duplicate",
"data": {
"name": "x",
"scope": "let"
}
}
}Specification outcome. Product runs are pending the 2.0 adapter.
Semantic details
{
"canonicalOutcomeProduced": false,
"evaluationReceiptEligible": false
}Product invocation
Expected result
Preparation rejects the repeated x name in the parallel binding group. Product invocation awaits its 2.0 adapter.
Initializer order cannot choose a winner for a repeated name.
Recursive bindings
A recursive procedure can call itself. Mutually recursive procedures can call one another. Recursion creates new calls with new argument bindings. It does not assign new values to earlier bindings.
5.7 Tie recursive names only to procedures
Each initializer in letrec must be a lambda expression. The procedure bodies can refer to every name in the recursive group. The bindings are established before any of those bodies is called. Recursive internal definitions follow the same restriction. Calculate other values with let or let*.
Why
Restricting recursive initialization to procedures avoids observable partially initialized values while preserving mutual recursion.
Call another procedure in the recursive group
lispex.example.v2.evaluation.mutual-recursion · rev. 1
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(letrec ((even-step (lambda (n)
(if (= n 0) #t (odd-step (- n 1)))))
(odd-step (lambda (n)
(if (= n 0) #f (even-step (- n 1))))))
(even-step input))))
)
Request and files
{
"notation": "documentation-request",
"source": "source.scm",
"semantics": {
"language": "Lispex 2.0",
"standardLibrary": "one fixed library identity",
"unicode": "one fixed Unicode semantics version",
"resourceModel": "one fixed finite resource model"
},
"limits": {
"prepare": "finite values sufficient to finish preparation checks",
"evaluate": "finite values sufficient for the stated finite calculation"
},
"requests": [
{
"input": {
"kind": "integer",
"value": "4"
}
}
],
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "source.scm"
}
]
}
#tOutcome details
{
"kind": "value",
"valueCount": 1,
"datum": {
"kind": "boolean",
"value": true
}
}Specification outcome. Product runs are pending the 2.0 adapter.
Product invocation
Expected result
The mutually recursive procedures return true for the exact integer 4. Product invocation awaits its 2.0 adapter.
Both initializers are lambda expressions. The first procedure body can refer to the second binding.
Reject a value initializer in letrec
lispex.example.v2.evaluation.recursive-value-rejected · rev. 3
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(letrec ((x 1))
x)))
)
Request and files
{
"notation": "documentation-request",
"source": "source.scm",
"semantics": {
"language": "Lispex 2.0",
"standardLibrary": "one fixed library identity",
"unicode": "one fixed Unicode semantics version",
"resourceModel": "one fixed finite resource model"
},
"limits": {
"prepare": "finite values sufficient to finish preparation checks",
"evaluate": "finite values sufficient for the stated finite calculation"
},
"requests": [
{
"input": {
"kind": "integer",
"value": "0"
}
}
],
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "source.scm"
}
]
}
binding.initializerOutcome details
{
"kind": "prepare-failure",
"valueCount": 0,
"diagnostic": {
"phase": "normalize",
"code": "binding.initializer",
"data": {
"form": "letrec",
"name": "x",
"expected": "lambda"
}
}
}Specification outcome. Product runs are pending the 2.0 adapter.
Semantic details
{
"entryEvaluated": false,
"canonicalOutcomeProduced": false,
"evaluationReceiptEligible": false
}Product invocation
Expected result
The request is rejected because the recursive binding has a non-lambda initializer. Product invocation awaits its 2.0 adapter.
A constant value does not become an allowed recursive initializer merely because it has no recursive reference.
Use an internal recursive procedure
lispex.example.v2.evaluation.internal-recursive-procedure · rev. 1
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(define countdown
(lambda (n)
(if (= n 0)
7
(countdown (- n 1)))))
(countdown input)))
)
Request and files
{
"notation": "documentation-request",
"source": "source.scm",
"semantics": {
"language": "Lispex 2.0",
"standardLibrary": "one fixed library identity",
"unicode": "one fixed Unicode semantics version",
"resourceModel": "one fixed finite resource model"
},
"limits": {
"prepare": "finite values sufficient to finish preparation checks",
"evaluate": "finite values sufficient for the stated finite calculation"
},
"requests": [
{
"input": {
"kind": "integer",
"value": "4"
}
}
],
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "source.scm"
}
]
}
7Outcome details
{
"kind": "value",
"valueCount": 1,
"datum": {
"kind": "integer",
"value": "7"
}
}Specification outcome. Product runs are pending the 2.0 adapter.
Product invocation
Expected result
The internal recursive procedure returns the exact integer 7. Product invocation awaits its 2.0 adapter.
The recursive internal definition has a lambda initializer. Its calls use fresh argument bindings.
5.8 Use named let for a recursive loop
A named let evaluates its initializer expressions from left to right in the enclosing scope. The new loop binding is visible in the loop body. It introduces a recursive procedure for that body and makes an initial call with the initializer values. Each recursive call creates new parameter bindings. The loop body can call the named procedure without replacing an earlier binding.
Why
Named let expresses repeated calculation with ordinary recursive procedures and immutable values.
Named let initializers are evaluated in the enclosing scope.
Accumulate a value through recursive arguments
lispex.example.v2.evaluation.named-let-sum · rev. 2
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(let loop ((n input)
(total 0))
(if (= n 0)
total
(loop (- n 1) (+ total n))))))
)
Request and files
{
"notation": "documentation-request",
"source": "source.scm",
"semantics": {
"language": "Lispex 2.0",
"standardLibrary": "one fixed library identity",
"unicode": "one fixed Unicode semantics version",
"resourceModel": "one fixed finite resource model"
},
"limits": {
"prepare": "finite values sufficient to finish preparation checks",
"evaluate": "finite values sufficient for the stated finite calculation"
},
"requests": [
{
"input": {
"kind": "integer",
"value": "4"
}
}
],
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "source.scm"
}
]
}
10Outcome details
{
"kind": "value",
"valueCount": 1,
"datum": {
"kind": "integer",
"value": "10"
}
}Specification outcome. Product runs are pending the 2.0 adapter.
Product invocation
Expected result
The loop returns the exact integer 10. Product invocation awaits its 2.0 adapter.
The total for each call is a new parameter binding. The previous total is never assigned another value.
Resolve an initializer before the new loop scope
lispex.example.v2.evaluation.named-let-initializer-scope · rev. 2
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(let ((loop 9))
(let loop ((n loop))
n))))
)
Request and files
{
"notation": "documentation-request",
"source": "source.scm",
"semantics": {
"language": "Lispex 2.0",
"standardLibrary": "one fixed library identity",
"unicode": "one fixed Unicode semantics version",
"resourceModel": "one fixed finite resource model"
},
"limits": {
"prepare": "finite values sufficient to finish preparation checks",
"evaluate": "finite values sufficient for the stated finite calculation"
},
"requests": [
{
"input": {
"kind": "integer",
"value": "0"
}
}
],
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "source.scm"
}
]
}
9Outcome details
{
"kind": "value",
"valueCount": 1,
"datum": {
"kind": "integer",
"value": "9"
}
}Specification outcome. Product runs are pending the 2.0 adapter.
Product invocation
Expected result
The initializer reads the enclosing loop binding and the rule returns the exact integer 9. Product invocation awaits its 2.0 adapter.
The new recursive procedure is visible in its body. It does not replace the enclosing value used to calculate the initial argument.
Tail calls
5.9 Transfer a tail result without retaining a caller frame
The final expression of a procedure body is in tail position. A selected conditional branch and the final expression of a begin or binding body inherit tail position from the enclosing expression. A guard body is not in tail position. The final expression of a selected handler clause body inherits tail position from the guard expression. A tail call passes its result directly to the pending caller. Its frame capacity follows the semantic-frame rule. Its work and logical allocation use those resource accounts.
The implicit target call of apply inherits the tail position of that apply call. The consumer call of call-with-values inherits the tail position of that call-with-values call. In tail position, the implicit call takes over the native call's frame and pending return. The replaced native call emits no call-return. The producer call and every other library callback are outside tail position.
Why
The number of completed tail steps must not become a hidden host stack limit. A loop expressed through apply has the same guarantee. Charging each step still guarantees finite execution.
An active guard must keep protecting its body. The enclosing call keeps its pending return if that body fails. Handler tail transfer is possible after guard deactivation.
Complete many tail calls with a fixed frame budget
lispex.example.v2.evaluation.tail-progress · rev. 1
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(let loop ((n input)
(total 0))
(if (= n 0)
total
(loop (- n 1) (+ total 1))))))
)
Request and files
{
"notation": "documentation-request",
"source": "source.scm",
"semantics": {
"language": "Lispex 2.0",
"standardLibrary": "one fixed library identity",
"unicode": "one fixed Unicode semantics version",
"resourceModel": "one fixed finite resource model"
},
"limits": {
"prepare": "finite values sufficient to finish preparation checks",
"evaluate": "the same finite work and allocation limits sufficient for both calculations",
"frames": "one fixed finite budget sufficient for the surrounding calls without retaining a frame for each tail step"
},
"requests": [
{
"input": {
"kind": "integer",
"value": "1"
}
},
{
"input": {
"kind": "integer",
"value": "1000"
}
}
],
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "source.scm"
}
]
}
1Outcome details
{
"kind": "value",
"valueCount": 1,
"datum": {
"kind": "integer",
"value": "1"
}
}1000Outcome details
{
"kind": "value",
"valueCount": 1,
"datum": {
"kind": "integer",
"value": "1000"
}
}Specification outcome. Product runs are pending the 2.0 adapter.
Semantic details
{
"tailTransfer": {
"retainedAdditionalReturnFrame": false
}
}Product invocation
Expected result
The two requests return the exact integers 1 and 1000 under the same finite frame budget. Product invocation awaits its 2.0 adapter.
The larger input requires more work. It does not require a pending return frame for every completed tail call.
Stop an unending tail loop at a resource limit
lispex.example.v2.evaluation.tail-loop-stops · rev. 2
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(let loop ()
(loop))))
)
Request and files
{
"notation": "documentation-request",
"source": "source.scm",
"semantics": {
"language": "Lispex 2.0",
"standardLibrary": "one fixed library identity",
"unicode": "one fixed Unicode semantics version",
"resourceModel": "one fixed finite resource model"
},
"limits": {
"prepare": "finite values sufficient to finish preparation checks",
"evaluate": "finite values sufficient to enter the loop"
},
"requests": [
{
"input": {
"kind": "integer",
"value": "0"
}
}
],
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "source.scm"
}
]
}
resource-stopOutcome details
{
"kind": "resource-stop",
"valueCount": 0,
"terminal": true,
"diagnosticIdentity": {
"phase": "evaluate",
"code": "resource.exhausted"
}
}Specification outcome. Product runs are pending the 2.0 adapter.
Semantic details
{
"returnedControl": true
}Product invocation
Expected result
The unending tail loop reaches a terminal resource stop and returns control without a result datum. Product invocation awaits its 2.0 adapter.
Eliminating retained return frames does not eliminate work charges or permit unbounded execution.
Finish a tail apply loop with a small frame budget
lispex.example.v2.evaluation.tail-apply-frames · rev. 1
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(let loop ((n input))
(if (= n 0)
7
(apply loop (list (- n 1))))))))
Request and files
{
"notation": "documentation-request",
"source": "source.scm",
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "source.scm"
}
],
"semantics": {
"language": "Lispex 2.0",
"standardLibrary": "one fixed library identity",
"unicode": "Unicode 17.0.0",
"resourceModel": "one fixed finite resource model"
},
"limits": {
"prepare": "finite values sufficient to prepare this source",
"evaluate": {
"work": "finite work sufficient for both complete requests",
"allocation": "finite allocation sufficient for both complete requests",
"frames": "one fixed small finite budget sufficient for module initialization, one active loop invocation, and its bounded operand calls and expression events, independent of the iteration count",
"channels": "finite channel limits sufficient for both requests and their result encoding"
}
},
"requests": [
{
"input": {
"kind": "integer",
"value": "1"
}
},
{
"input": {
"kind": "integer",
"value": "1000"
}
}
]
}
7Outcome details
{
"kind": "value",
"valueCount": 1,
"datum": {
"kind": "integer",
"value": "7"
}
}7Outcome details
{
"kind": "value",
"valueCount": 1,
"datum": {
"kind": "integer",
"value": "7"
}
}Specification outcome. Product runs are pending the 2.0 adapter.
Semantic details
{
"frameBehavior": {
"sameFrameLimit": true,
"applyTargetTail": true,
"nativeFrameTransferred": true,
"replacedNativeCallReturn": false,
"additionalReturnFramesPerIteration": false
}
}Product invocation
Expected result
Both requests return 7 under the same small frame budget. Every apply target inherits tail position and takes over the native frame. No replaced apply call emits call-return. Product invocation awaits its 2.0 adapter.
The request and site labels are documentation notation. Symbolic finite limits come from the selected resource contract. They are not measurements copied from a run.
Stop a nontail apply loop at its frame limit
lispex.example.v2.evaluation.nontail-apply-frames · rev. 1
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(let loop ((n input))
(if (= n 0)
0
(+ 1 (apply loop (list (- n 1)))))))))
Request and files
{
"notation": "documentation-request",
"source": "source.scm",
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "source.scm"
}
],
"semantics": {
"language": "Lispex 2.0",
"standardLibrary": "one fixed library identity",
"unicode": "Unicode 17.0.0",
"resourceModel": "one fixed finite resource model"
},
"limits": {
"prepare": "finite values sufficient to prepare this source",
"evaluate": {
"work": "finite work sufficient for the complete recursion if frames permit it",
"allocation": "finite allocation sufficient for the complete recursion if frames permit it",
"frames": "a small finite budget sufficient for module initialization and at least one recursive step but insufficient for the full chain of pending calls for input 1000",
"channels": "finite channel limits sufficient for the request and terminal diagnostic"
}
},
"requests": [
{
"input": {
"kind": "integer",
"value": "1000"
}
}
]
}
resource-stop · framesOutcome details
{
"kind": "resource-stop",
"phase": "evaluate",
"axis": "frames",
"terminal": true,
"valueCount": 0
}Specification outcome. Product runs are pending the 2.0 adapter.
Semantic details
{
"frameBehavior": {
"applyTargetTail": false,
"nativeContinuationRetained": true,
"pendingReturnFramesGrowWithRecursion": true,
"baseCaseReached": false
}
}Product invocation
Expected result
The apply call is an argument of addition. Its target inherits nontail position and retains the native continuation. The small frame budget stops recursion before the base case. Product invocation awaits its 2.0 adapter.
The request and site labels are documentation notation. Symbolic finite limits come from the selected resource contract. They are not measurements copied from a run.
Transfer a call-with-values frame to its consumer
lispex.example.v2.evaluation.tail-consumer-frames · rev. 1
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(let loop ((n input))
(if (= n 0)
7
(call-with-values
(lambda () (- n 1))
loop))))))
Request and files
{
"notation": "documentation-request",
"source": "source.scm",
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "source.scm"
}
],
"semantics": {
"language": "Lispex 2.0",
"standardLibrary": "one fixed library identity",
"unicode": "Unicode 17.0.0",
"resourceModel": "one fixed finite resource model"
},
"limits": {
"prepare": "finite values sufficient to prepare this source",
"evaluate": {
"work": "finite work sufficient for the complete request",
"allocation": "finite allocation sufficient for the complete request",
"frames": "one small finite budget sufficient for module initialization, one active loop invocation, and its bounded producer and operand work, independent of the iteration count",
"channels": "finite channel limits sufficient for the request and result encoding"
}
},
"requests": [
{
"input": {
"kind": "integer",
"value": "1000"
}
}
]
}
7Outcome details
{
"kind": "value",
"valueCount": 1,
"datum": {
"kind": "integer",
"value": "7"
}
}Specification outcome. Product runs are pending the 2.0 adapter.
Semantic details
{
"frameBehavior": {
"producerTail": false,
"producerReturnsBeforeConsumer": true,
"consumerTail": true,
"nativeFrameTransferred": true,
"replacedNativeCallReturn": false,
"additionalReturnFramesPerIteration": false
}
}Product invocation
Expected result
The loop returns 7 under a small frame budget. Each producer call is nontail and finishes before its consumer. Each consumer inherits tail position and takes over the native frame. Product invocation awaits its 2.0 adapter.
The request and site labels are documentation notation. Symbolic finite limits come from the selected resource contract. They are not measurements copied from a run.
Stop guarded body recursion at the frame limit
lispex.example.v2.evaluation.guard-body-frame-stop · rev. 1
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(let loop ()
(guard (condition
(else 99))
(loop)))))
)
Request and files
{
"notation": "documentation-request",
"source": "source.scm",
"semantics": {
"language": "Lispex 2.0",
"standardLibrary": "one fixed library identity",
"unicode": "one fixed Unicode semantics version",
"resourceModel": "one fixed finite resource model"
},
"limits": {
"prepare": "finite values sufficient to prepare this source",
"evaluate": "finite work and allocation sufficient through the first frames refusal. Every other axis and channel is sufficient.",
"frames": "one fixed finite budget sufficient to enter the first guard and make one recursive call"
},
"requests": [
{
"input": {
"kind": "integer",
"value": "0"
}
}
],
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "source.scm"
}
]
}
resource-stop · framesOutcome details
{
"kind": "resource-stop",
"valueCount": 0,
"terminal": true,
"axis": "frames",
"diagnosticIdentity": {
"phase": "evaluate",
"code": "resource.exhausted"
}
}Specification outcome. Product runs are pending the 2.0 adapter.
Semantic details
{
"recovery": {
"clauseSelected": false,
"guestCleanupEntered": false
},
"returnedControl": true
}Product invocation
Expected result
Recursive calls in the protected body exhaust the fixed frame budget. The terminal resource stop does not enter the handler. Product invocation awaits its 2.0 adapter.
Each recursive call keeps its active guard and pending return. The resource model selects the first refused frame reservation. The example fixes no tariff or failing depth.
Repeat handler tail calls within one frame budget
lispex.example.v2.evaluation.guard-handler-tail-progress · rev. 1
(module main
(import)
(export rule)
(entry rule)
(define rule
(lambda (input)
(let loop ((remaining input))
(if (= remaining 0)
0
(guard (condition
(else (loop (- remaining 1))))
(car '()))))))
)
Request and files
{
"notation": "documentation-request",
"source": "source.scm",
"semantics": {
"language": "Lispex 2.0",
"standardLibrary": "one fixed library identity",
"unicode": "one fixed Unicode semantics version",
"resourceModel": "one fixed finite resource model"
},
"limits": {
"prepare": "finite values sufficient to prepare this source",
"evaluate": "the same finite work and allocation limits sufficient for both complete requests. Every channel is sufficient.",
"frames": "one fixed finite budget sufficient for the surrounding calls and one complete loop step. It covers the active guard and transient calls."
},
"requests": [
{
"input": {
"kind": "integer",
"value": "1"
}
},
{
"input": {
"kind": "integer",
"value": "1000"
}
}
],
"rootModule": "main",
"moduleSources": [
{
"name": "main",
"source": "source.scm"
}
]
}
0Outcome details
{
"kind": "value",
"valueCount": 1,
"datum": {
"kind": "integer",
"value": "0"
}
}0Outcome details
{
"kind": "value",
"valueCount": 1,
"datum": {
"kind": "integer",
"value": "0"
}
}Specification outcome. Product runs are pending the 2.0 adapter.
Semantic details
{
"tailTransfer": {
"retainedAdditionalReturnFrame": false
},
"recovery": {
"handlingGuardActive": false,
"failedOperationResumed": false
},
"frameBehavior": {
"recursiveTailCallsAddCallFrames": false,
"completedHandlersRetainGuardFrames": false
}
}Product invocation
Expected result
Both requests return the exact integer 0 under the same finite frame budget. Completed handler tail calls retain no extra call or guard frames. Product invocation awaits its 2.0 adapter.
Each car failure selects the handler after guard deactivation. The handler tail call reuses the enclosing call frame. Work and logical allocation still accumulate.