If a word calls a combinator, one of the following 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. |
| • | In a non-inline word with a fixed stack effect, a quotation input with a monomorphic effect annotation can be called. The compiler inserts a checked static call, including when the parameter is passed through locals, curry or compose. This supplies a call contract, not a literal value for arbitrary macro expansion. |
If none of these conditions 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 effectsInline 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.
ExamplesCalling a combinatorThe 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 combinatorThe 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 quotationsA fixed quotation input annotation lets a non-inline word use
call:
: apply-one ( x quot: ( x -- y ) -- y ) call ;
This compiles the call with the same runtime checking as
call( x -- y ). Row-polymorphic quotation parameters still require inlining.
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 combinatorSuppose 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 ;
ExplanationThis 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.
Passing quotations through recursive combinatorsA known quotation remains known after an inline recursive combinator such as
each or
map returns, provided its body never accesses that value in the caller's stack prefix:
[ [ reverse ] swap [ reverse ] map swap call ] infer.
( x -- x )
The quotation can also be held with
dip:
[ [ reverse ] [ [ reverse ] map ] dip call ] infer.
( x -- x )