# Lesson V
To paint truly, first know the ink.
## Try
```prolog
% ?- word(add, In -- Out, Goal).
% In = [_A::int, _B::int|_S],
% Out = [_C::int|_S],
% Goal = (_C is _B + _A).
% ?- apply(add, [3, 2], Stack).
% Stack = [5].
% ?- run([dup, add], [2], Stack).
% Stack = [4]
% ?- infer([dup, add], In, Out).
% In = [int|_S],
% Out = [int|_S].
```
## Learn
```prolog
% Arithmetic is the first primitive family that has to inspect
% payloads. With stack `[3, 2]`, `3` is the top item and `2` is below
% it, so `add` computes `2 + 3`.
%
% The same row says three things:
%
% * runtime should unpack two integer payloads and compute a third;
% * inference should require two `int` slots;
% * the rest of the stack `S` is preserved.
:- op(650, xfx, ::).
word(add, [A::int, B::int|S] -- [C::int|S], C is B + A).
word(sub, [A::int, B::int|S] -- [C::int|S], C is B - A).
word(mul, [A::int, B::int|S] -- [C::int|S], C is B * A).
% Runtime now needs more than raw unification. A typed pattern cell
% must check the object-language type of the input value, expose a
% Prolog payload for the primitive goal, and put the computed payload
% back onto the stack as an object-language value.
%
% This is where the interpreter starts decoupling host representation
% from language meaning. The row for `add` says it consumes two `int`
% payloads and produces one `int` payload. It does not say that
% "Prolog integer" and "language int" are the same concept.
% `unpack/3` and `pack/3` keep that fact local:
%
% unpack(int, int(5), 5)
% pack(int, 5, int(5))
%
% Only the boundary changes.
apply(Word, Stack0, Stack) :-
word(Word, InPattern -- OutPattern, Goal),
bind(InPattern, Stack0),
call(Goal),
build(OutPattern, Stack).
% Static inference projects directly from the same primitive row.
% There is no extra static table: the type-level reading is just
% `word/3` plus pattern erasure.
infer([], Stack, Stack).
infer([Word|Words], Stack0, Stack) :-
word(Word, InPattern -- OutPattern, _Goal),
types(InPattern, Stack0),
types(OutPattern, Stack1),
infer(Words, Stack1, Stack).
% Pattern projection erases runtime payload variables to obtain type
% stacks. A variable tail such as `S` must remain a variable tail;
% otherwise every primitive would accidentally require a closed stack.
types(Stack, Stack) :-
var(Stack),
!.
types([], []).
types([Item|Items], [Type|Types]) :-
typed(Item, _Payload, Type),
!,
types(Items, Types).
types([Type|Items], [Type|Types]) :-
types(Items, Types).
% `typed/3` is introduced here as a small recognizer for the new
% pattern form.
%
% The `nonvar/1` guard is part of that scaffolding. It says: only
% treat a pattern item as `Payload::Type` when the primitive row
% already wrote it that way. Open whole-cell variables such as the `A`
% in `dup` should remain whole stack cells, not be eagerly
% reinterpreted as payload views.
typed(Item, Payload, Type) :-
nonvar(Item),
Item = (Payload::Type).
% Runtime binding is the input half of the runtime projection. A
% whole-item pattern such as `A` matches the whole stack cell. A typed
% payload pattern such as `A::int` asks whether the cell is an
% object-language `int`, then binds `A` to the host payload that the
% arithmetic goal should see.
%
% In this stage the value and payload are both the same Prolog
% integer, but that is an implementation coincidence. `unpack/3`
% names the act of crossing from the language's value world into
% Prolog's computation world. For `add`, the first two bindings are:
%
% A::int with stack value 3 gives payload A = 3
% B::int with stack value 2 gives payload B = 2
%
% The arithmetic goal receives `A` and `B`, not the original pattern
% cells.
bind(Pattern, Stack) :-
var(Pattern),
!,
Pattern = Stack.
bind([], []).
bind([Item|Patterns], [Value|Values]) :-
typed(Item, Payload, Type),
!,
unpack(Type, Value, Payload),
bind(Patterns, Values).
bind([Value|Patterns], [Value|Values]) :-
bind(Patterns, Values).
% Runtime building is the output half. The primitive goal computes a
% host payload such as `5`; the output pattern says that the result
% must return to the stack as an object-language `int` value.
%
% For now it uses raw Prolog integers as object-language integers,
% so packing `int` is only validation plus identity. The important
% part is that `build/2` never treats the Prolog result as self-typed.
% It passes the payload through `pack/3`, where the interpreter
% decides what value the object language receives. For `add`, after
% `C is B + A` computes `C = 5`, the output pattern `C::int` asks
% `pack/3` to produce the stack cell.
build(Pattern, Stack) :-
var(Pattern),
!,
Stack = Pattern.
build([], []).
build([Item|Patterns], [Value|Values]) :-
typed(Item, Payload, Type),
!,
pack(Type, Payload, Value),
build(Patterns, Values).
build([Value|Patterns], [Value|Values]) :-
build(Patterns, Values).
% These two clauses are small because there is only one concrete type
% and its value representation is still bare. Their significance is
% structural, not computational: primitive goals work with host
% payloads, stacks contain object-language values, and only this pair
% translates between the two.
unpack(int, Value, Value) :-
integer(Value).
pack(int, Payload, Payload) :-
integer(Payload).
```