example.com/path/to/article
000 points · username · 0 hours ago
example.com0 points · 0 comments · 7 years ago · triska
For comparison, here is a small interpreter for a subset of Prolog, written in Prolog:
mi([]).
mi([G|Gs]) :-
clause_(G, Body),
mi(Body),
mi(Gs).
This states that: First, the empty conjunction (of goals) is true, and second, if there is at least one goal left, than the conjunction is true if there is a suitable clause whose body evaluates to true, and the same holds for all remaining goals.For example, we can define append/3 as follows in this representation:
clause_(append([], Ys, Ys), []).
clause_(append([X|Xs], Ys, [X|Zs]), [append(Xs,Ys,Zs)]).
and then evaluate it with our interpreter: ?- mi([append([a,b,c], [d,e], Ls)]).
Ls = [a, b, c, d, e].
Characteristically, it also works in other directions. For example: ?- mi([append([a,b,c], Rs, [a,b,c,d,e])]).
Rs = [d, e].
and also: ?- mi([append(As, Bs, [x,y])]).
As = [],
Bs = [x, y] ;
As = [x],
Bs = [y] ;
As = [x, y],
Bs = [] ;
false.
Now the point: The clauses of the meta-interpreter itself can also be stated in this form, namely as: clause_(mi([]), []).
clause_(mi([G|Gs]), [clause_(G,Body),mi(Body),mi(Gs)]).
Now we only have to define what clause_/2 means, which we can do with: clause_(clause_(G, Body), []) :- clause_(G, Body).
And now we can use our meta-interpreter to evaluate its own code as it interprets a program, arbitrarily deeply layered: ?- mi([mi([mi([append([a,b,c],[d,e],Ls)])])]).
Ls = [a, b, c, d, e].
Hence, Prolog admits a meta-circular evaluator in at most 5 lines of code. With a better representation for clauses (using list differences), you can reduce the interpreter to 4 lines of code and at the same time make it tail-recursive.YeGoblynQueenne