Search code examples
quotationsfactor-lang

Using quotation computed by another word causes compilation error


Background

Loading the snippet below results in an error message Cannot apply "call" to a run-time computed value:

: subtract-sum ( seq -- quot: ( n -- n ) ) sum '[ _ - ] ;
: subtract-sum-seq ( seq -- x ) dup subtract-sum map ;

My understanding is that this is an expected behavior, since internal call to call in map requires inputs and outputs of processed quotation to be present at compile time.

Problem

However I tested in listener what I believe to be two equivalent expressions and they worked just fine.

Example 1:

# : subtract-sum ( seq -- quot: ( n -- n ) ) sum '[ _ - ] ;
# : subtract-sum-seq ( seq -- seq call ) dup subtract-sum ;
# { 1 2 3 4 } subtract-sum-seq
{ 1 2 3 4 }
[ 10 - ]
# map
{ -9 -8 -7 -6 }

Example 2:

# : subtract-sum-seq ( seq -- x ) dup '[ _ - ] map ;
# { 1 2 3 4 } subtract-sum-seq
{ -9 -8 -7 -6 }

Question

What is the difference between original code and the working examples that causes an error in the first one but not the other two? There clearly seems to be something about quotations I'm not understanding here.

Additional info

Interestingly, I tried to wrap my call to map inside the listener from the first example into a word and it resulted in the same error as the original code:

# { 1 2 3 4 } subtract-avg-seq map
{ -9 -8 -7 -6 }
# : apply ( -- seq ) { 1 2 3 4 } subtract-avg-seq map ; ! error: Cannot apply "call" to a run-time computed value

Solution

  • There are two different problems at play in this example.

    The first is that in interactive code, the Listener will not check the stack effect of quotations, but they get checked when the code gets compiled in a definition. That's the reason that manually expanding the words in the Listener worked.

    The second problem is that the nested effects declared for the quotations are ignored in most words. You could replace the ( n -- quot: ( n -- n ) ) with ( n -- q ) and it will work the same.

    In this case, the declaration for the quotation in the first word doesn't carry to the second word. That's the reason that even if it's all theoretically correct, the compiler can't prove it; it just doesn't know the effects of the quotation.

    The solution is to declare the effects of the quotation at the call site:

    : subtract-sum ( seq -- quot: ( n -- n ) ) sum '[ _ - ] ;
    : subtract-sum-seq ( seq -- x ) dup subtract-sum [ call( n -- n ) ] curry map ;
    { 1 2 3 4 } subtract-sum-seq .
    
    ! -> { -9 -8 -7 -6 }
    

    https://docs.factorcode.org/content/article-effects.html

    https://docs.factorcode.org/content/article-inference-escape.html