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 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 quotationsIn 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.
LimitationsThe 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 )