# Lesson IX A stroke begins by knowing where it will appear. ## Try ```prolog % ?- empty_env(E), % infer(E, [push(quote([dup, mul])), call], Effect). % Effect = effect([int|S], [int|S]). % ?- empty_env(E), % run(E, [push(int(4)), push(quote([dup, mul])), call], [], Stack). % Stack = [int(16)]. ``` ## Learn ```prolog % Runtime sequencing is ordinary stack threading over a word list. run(_, [], Stack, Stack). run(Env, [Word|Words], Stack0, Stack) :- apply(Env, Word, Stack0, Stack1), run(Env, Words, Stack1, Stack). % Fixed words project through primitive rows. apply(_Env, Word, Stack0, Stack) :- word(Word, InPattern -- OutPattern, Goal), bind(InPattern, Stack0), call(Goal), build(OutPattern, Stack). % `push/1` validates a literal and puts it on the runtime stack % unchanged. apply(Env, push(Value), Stack, [Value|Stack]) :- lit(Env, Value, _Type). apply(Env, call, [quote(Words)|Stack0], Stack) :- run(Env, Words, Stack0, Stack). % Static sequencing publishes an input stack and an output stack. % Public inference now returns an `effect/2` term. That term can % appear by itself or inside the type of a quotation. infer(_, [], effect(Stack, Stack)). infer(Env, [Word|Words], effect(Stack0, Stack)) :- infer1(Env, Word, Stack0, Stack1), infer(Env, Words, effect(Stack1, Stack)). % `push/1` contributes the literal's type to the static stack. infer1(Env, push(Value), Stack, [Type|Stack]) :- !, lit(Env, Value, Type). infer1(_Env, call, [quote(Effect)|Stack0], Stack) :- !, copy_term(Effect, effect(Stack0, Stack)). infer1(_Env, Word, Stack0, Stack) :- word(Word, InPattern -- OutPattern, _Goal), types(InPattern, Stack0), types(OutPattern, Stack). % `lit/3` is the only way a literal enters runtime or inference. lit(_Env, int(Int), int) :- integer(Int). % A quotation is valid when its body has a static stack effect. lit(Env, quote(Words), quote(effect(In, Out))) :- infer(Env, Words, effect(In, Out)). empty_env(env([])). ```