Skip to content

Basic evaluation ​

Let us see how MeTTa turns one expression into another. The whole language is built on a single idea: an expression is reduced by applying equality rules until none apply.

Programs are atoms ​

A script is parsed atom by atom into the space. An atom on its own is stored; an atom with ! is evaluated and its result printed. Here both happen:

metta
; This line is a comment, ignored by the parser.
Hello              ; a symbol, added to the space
(Hello World)      ; an expression, added to the space
!(+ 1 2)           ; evaluated immediately -> 3
!(Hi there)        ; evaluated immediately -> (Hi there)

(+ 1 2) reduces to 3 because + is a grounded operation that runs on its arguments. But (Hi there) reduces to itself: Hi is just a symbol, not an operation, so there is nothing to run. A bare symbol heading an expression behaves like a data constructor, the way Cons does in a functional language. This is why facts like (Likes Alice Pizza) are values, not computations.

Equalities are your functions ​

To make an expression reduce to something other than itself, define an equality with =:

metta
(= (greet) (Hello World))
!(greet)           ; (Hello World)

(= (greet) (Hello World)) says "(greet) can be reduced to (Hello World)". Equalities look like function definitions, but they differ in important ways.

They need not be total. A rule can match only some inputs, and an unmatched call is simply left alone:

metta
(= (only-a A) accepted)
!(only-a A)        ; accepted
!(only-a B)        ; (only-a B): no rule matches, so it is left unreduced

There is no error for (only-a B); it is just a value the interpreter could not reduce. In MeTTa there is no hard line between a function and a data constructor.

Order matters relative to definition. A call is reduced using the rules that exist at that point in the program:

metta
!(respond me)              ; (respond me): no rule yet
(= (respond me) ok)
!(respond me)              ; ok

Parameters and patterns ​

Rules take variables, and the bound values are substituted into the right-hand side:

metta
(= (duplicate $x) ($x $x))
!(duplicate A)     ; (A A)

The left-hand side can have structure, so you get pattern matching for free:

metta
(= (swap (Pair $x $y)) (Pair $y $x))
!(swap (Pair A B)) ; (Pair B A)

It is more general than functional-language matching, because the pattern can be any shape, and a variable may even repeat. A repeated variable only matches when both positions are equal:

metta
(= (check ($x $y $x)) ($x $y))
!(check (B A B))   ; (B A)
!(check (B A A))   ; (check (B A A)): does not match, left unreduced

Several results ​

A head can have more than one rule, and they are not mutually exclusive. Evaluation is nondeterministic: every applicable rule contributes a result.

metta
(= (bin) 0)
(= (bin) 1)
!(bin)             ; 0 and 1, both

This even applies when a specific rule and a general rule both match:

metta
(= (f special) caught)
(= (f $x) $x)
!(f A)             ; A
!(f special)       ; caught and special, both

Evaluation keeps going ​

A result is reduced further, for both symbolic and grounded steps:

metta
(= (square $x) (* $x $x))
!(square 3)        ; (square 3) -> (* 3 3) -> 9

Arguments are normally evaluated before the call, just like in most languages:

metta
!(* (+ 1 2) (- 8 3))   ; 15
(= (square $x) (* $x $x))
!(square (+ 2 3))      ; 25

That is the whole evaluation model: match a rule, substitute, reduce the result, repeat. Next we use it for repetition and choices: Recursion and control.

Released under the MIT License.