Combinator stack effects
Factor handbook » The language » Stack effect checking

Prev:Straight-line stack effects
Next:Recursive combinator stack effects


If a word calls a combinator, one of the following two conditions must hold for the stack checker to succeed:
The combinator must be called with a quotation that is either literal or built from literal quotations, curry, and compose. (Note that quotations that use fry or locals use curry and compose from the perspective of the stack checker.)
If the word is declared inline, the combinator may additionally be called on one of the word's input parameters or with quotations built from the word's input parameters, literal quotations, curry, and compose. When inline, a word is itself considered to be a combinator, and its callers must in turn satisfy these conditions.

If neither condition holds, the stack checker throws an unknown-macro-input or bad-macro-input error. To make the code compile, a runtime checking combinator such as call( must be used instead. See Stack effect checking escape hatches for details. An inline combinator can be called with an unknown quotation by currying the quotation onto a literal quotation that uses call(.

Input stack effects
Inline combinators will verify the stack effect of their input quotations if they are declared in the combinator's stack effect. See Stack effect row variables for details.

Examples

Calling a combinator
The following usage of map passes the stack checker, because the quotation is the result of curry:
USING: math sequences ; [ [ + ] curry map ] infer.
( x x -- x )

The equivalent code using fry and locals likewise passes the stack checker:
USING: fry math sequences ; [ '[ _ + ] map ] infer.
( x x -- x )

USING: locals math sequences ; [| a | [ a + ] map ] infer.
( x x -- x )


Defining an inline combinator
The following word calls a quotation twice; the word is declared inline, since it invokes call on the result of compose on an input parameter:
: twice ( value quot -- result ) dup compose call ; inline

The following code now passes the stack checker; it would fail were twice not declared inline:
USE: math.functions [ [ sqrt ] twice ] infer.
( x -- x )


Defining a combinator for unknown quotations
In the next example, call( must be used because the quotation is the result of calling a runtime accessor, and the compiler cannot make any static assumptions about this quotation at all:
TUPLE: action name quot ; : perform ( value action -- result ) quot>> call( value -- result ) ;


Passing an unknown quotation to an inline combinator
Suppose we want to write:
: perform ( values action -- results ) quot>> map ;

However this fails to pass the stack checker since there is no guarantee the quotation has the right stack effect for map. It can be wrapped in a new quotation with a declaration:
: perform ( values action -- results ) quot>> [ call( value -- result ) ] curry map ;


Explanation
This restriction exists because without further information, one cannot say what the stack effect of call is; it depends on the given quotation. If the stack checker encounters a call without further information, a unknown-macro-input or bad-macro-input error is raised.

On the other hand, the stack effect of applying call to a literal quotation or a curry of a literal quotation is easy to compute; it behaves as if the quotation was substituted at that point.

Limitations
The stack checker cannot guarantee that a literal quotation is still literal if it is passed on the data stack to an inlined recursive combinator such as each or map. For example, the following will not infer:
[ [ reverse ] swap [ reverse ] map swap call ] infer.
Cannot apply “call” to a run-time computed value macro call

To make this work, use dip to pass the quotation instead:
[ [ reverse ] [ [ reverse ] map ] dip call ] infer.
( x -- x )