Thank you very much for the publicity, I greatly appreciate it!
Prolog is rather unique in that it admits a meta-interpreter in roughly 3 lines of code.
Contrast this with other languages, where writing an evaluator typically takes much more code, even for quite high-level languages.
This brevity is possible mainly because Prolog - like Lisp and assembly language - is a homoiconic language, and in addition to that, Prolog has powerful built-in mechanisms that can be used in interpreters, and in addition to that, Prolog variables on the object-level (the program to be interpreted) are also variables on the meta-level (the interpreter). As a consequence, Prolog's implicit mechanisms can be absorbed in interpreters: They need not be encoded explicitly.
Can you explain to me what you use Prolog for and why you chose it over something more popular? What are the pros/cons in your mind?
Everytime I get started, I eventually stop when I realize I'm not an alien named Spock :). In all seriousness, it is very challenging to me, so kudos to you. My bucket list has Forth, APL, Lisp, Smalltalk, and Prolog on it.
Learning Prolog was very challenging for me too! I was extremely lucky though in that I had one of the world's greatest Prolog experts as instructor, using the world's most sophisticated Prolog teaching environment:
For me, a major attraction of Prolog is the balance it strikes between readability, versatility and convenience. I know languages that sometimes admit shorter programs, but they then are - at least in my experience - not as readable as a Prolog version, or not as versatile.
I am using Prolog for many different tasks, such as hosting web pages, prototyping showcases, and solving combinatorial optimization problems.
I have to make extensive use of Linear Programming & Mixed-Integer Linear Programming in my day job. This all runs on CPLEX. It would be really cool to do that sort of thing in Prolog, but I doubt it could handle massive models. Anyway you could explain what combinatorial optimization is for and how it differs from the things commonly seen in operations research?
For solving linear programs and mixed-integer linear programs with Prolog, I recommend the CLP(Q) and CLP(R) libraries of SICStus Prolog. See for example:
For discrete optimization tasks, also check out the integer constraint solver that ships with SICStus Prolog. See especially the combinatorial constraints:
As to the relation between Prolog and operations research: Prolog systems internally use methods from operations research to efficiently solve optimization tasks; in addition to that, you can of course also implement every OR algorithm in Prolog.
For many commercial users of SICStus Prolog, the efficiency of its constraint solvers are an important reason for buying a licence.
The three or so times I've needed to build a metainterpreter, I have always referred back to your page for help, so thanks for taking the time to make it! It's greatly appreciated!
Your "Power of Prolog" book/site has been a great help to me just this last week or so. I had developed a simple type inference algorithm for a stack-based language called Joy, using knowledge of unification gained from studying a Python implementation of miniKanren. At some point I realized that, if I wanted to do anything "fancy" with my types like propagating constraints or something, I should switch to Prolog (or Kanren.)
> Prolog has powerful built-in mechanisms that can be used in interpreters, and in addition to that, Prolog variables on the object-level (the program to be interpreted) are also variables on the meta-level (the interpreter). As a consequence, Prolog's implicit mechanisms can be absorbed in interpreters: They need not be encoded explicitly.
Brother, you're not kidding! I reimplemented the semantics of Joy in Prolog and when I had boiled off the cruft included due to my ignorance an inexperience with the language the resulting interpreter was very elegant. Several pages of Python became about a half-page of Prolog.
Not only that, but when I went to implement the type inferencer I realized I had just reimplemented the interpreter, only it was more elegant. In Prolog, the interpreter and the type inferencer are the same thing. (I feel foolish for not catching on and using Prolog, et. al. sooner.)
For what it's worth, here's the Joy-in-Prolog code (the library of functions is incomplete but the thun() relation works.):
Wow! This is very elegant and general Prolog code, thank you for sharing it, and for your kind words!
Regarding the handling of ">" and "<", one possible alternative is to use constraint reification to reason about the truth values of arithmetic relations.
For instance, we can equivalently describe the semantics of ">" as:
func(>, [A,B|S], [T|S]) :- B #> A #<==> R, r_truth(R, T).
r_truth(0, false).
r_truth(1, true).
As in the context of meta-interpreters, reification also here means to "make it a thing" (from Latin res, rei f.: "thing", "matter") - in this case, to reflect the constraint's meta-level truth in a Boolean value on the object-level.
> Wow! This is very elegant and general Prolog code, thank you for sharing it, and for your kind words!
Thank you! You're very kind but the credit goes to Manfred von Thun, he came up with Joy. And your book has helped a lot.
> Regarding the handling of ">" and "<", one possible alternative is to use constraint reification to reason about the truth values of arithmetic relations.
This is exactly what I wanted! Thank you. I'm just starting out with Prolog so it's difficult to know what to reach for, I spent twenty minutes looking for docs on "true" and "false" before realizing they weren't built in! Heh.
?- thun([dup, 0, >, [+], [-], branch], Si, So).
Si = [_9700, _9706|_9708],
So = [_9724|_9708],
_9700 in 1..sup,
_9724+_9700#=_9706 ;
Si = [_9800, _9806|_9808],
So = [_9824|_9808],
_9800 in inf..0,
_9800+_9806#=_9824 ;
false.
Awesome. (Both solutions, ta-daaa!)
Based on what you recommend about avoiding "defaulty" representations I've added a literal predicate:
I know this is not the done thing, but a meta-interpreter is the best, easiest, simplest way I've found to explain resolution (for maximum impact, you can trace a program through the meta-interpeter so the other person gets to see what's going on).
And if I can be allowed a shamless plug of my research team's work, it's also possible to turn a meta-interpreter to an inductive learning procedure, so that you can generate a new program from examples of its inputs and outputs, even as you prove it. It's called Meta-Interpetive Learning:
Sorry to be rude, but could you ask/beg Prof Muggleton to write a book on ILP and/or meta-interpretive learning? Something like an expanded version of his outline webpage (https://www.doc.ic.ac.uk/~shm/ilp_theory.html).
My go-to for trying to understand ILP theory is Nienhuys-Cheng and de Wolf, _Foundations of Inductive Logic Programming_ and De Raedt's _Logical and Relational Learning_ is a more up-to-date overview, but I feel like there's room for a more modern textbook. This new MIL stuff seems to be be an important unifying idea that also connects back to abductive logic programming, if I understand correctly.
Not rude at all! There are precious few textbooks on ILP, it's mostly a bunch of scattered papers published in various journals and conferences. Nienhuys-Cheng and de Wolf are an excellent first point of call. You can also try the following books:
Unfortunately, there are, as of yet, no textbooks on Meta-Interpretive Learning and I don't expect there to be one for a few years still. MIL is a very new area of research. The first papers were published only in 2015 and we are basically still working on it furiously. It will take a while before there is enough material for a textbook. All the same, I am planning to do a few internet postings regarding MIL, at some point in the near future.
Please feel free to contact me via my profile email, if you would like some pointers to MIL and more general ILP resources other than the ones discussed above.
Prolog is rather unique in that it admits a meta-interpreter in roughly 3 lines of code. Contrast this with other languages, where writing an evaluator typically takes much more code, even for quite high-level languages.
This brevity is possible mainly because Prolog - like Lisp and assembly language - is a homoiconic language, and in addition to that, Prolog has powerful built-in mechanisms that can be used in interpreters, and in addition to that, Prolog variables on the object-level (the program to be interpreted) are also variables on the meta-level (the interpreter). As a consequence, Prolog's implicit mechanisms can be absorbed in interpreters: They need not be encoded explicitly.
Enjoy!