Prolog: "Mistakes were made"
As an avid Prolog fan, I would have to agree with a lot of Mr. Wayne's comments! There are some things about the language that are now part of the ISO standard that are a bit unergonomic.
On the other hand, you don't have to write Prolog like that! The only shame is that there are 10x more examples (at least) of bad Prolog on the internet than good Prolog.
If you want to see some really beautiful stuff, check out Power of Prolog[1] (which Mr. Wayne courteously links to in his article!)
If you are really wondering why Prolog, the thing about it that makes it special among all languages is metainterpretation. No, seriously, would strongly recommend you check it out[2]
This is all that it takes to write a metainterpreter in Prolog:
mi1(true).
mi1((A,B)) :-
mi1(A),
mi1(B).
mi1(Goal) :-
Goal \= true,
Goal \= (_,_),
clause(Goal, Body),
mi1(Body).
Writing your own Prolog-like language in Prolog is nearly as fundamental as for-loops in other language.[1] https://www.youtube.com/@ThePowerOfProlog
https://www.metalevel.at/prolog
https://github.com/Seeker04/plwm
It actually has quite good UX affordances. More than that, however, I find the code imminently hackable, even as someone with very little Prolog experience. Reading through the plwm code really demystified the apparent gap between toy and practical Prolog for me. Heck, even the SWI-Prolog codbase itself is quite approachable!
I'm also mildly surprised at some of OG's gripes. A while back, I ran through Triska's The Power of Prolog[0], which crisply grounds Prolog's mental model and introduces standard conventions. In particular, it covers desugaring syntax into normal predicates, e.g. -/2 as pairs, [,]/2 as special syntax for ./2 cons cells, etc. Apparently, I just serendipitously stumbled into good pedagogical resources!
I'd be interested in ways that people utilize logical programming concepts and techniques into non-LP languages.
Prolog is also unusual in a sense that it is essential to understand what the interpreter does with your code in order to be able to write it well. For vanilla Prolog, that's not so hard. However, when constraint programming and other extensions are added, that becomes much harder to do.
To me, it feels like a data description language that someone discovered could be tricked into performing computation.
Just kidding. Some of those are stylistic choices I don't have gripes but can understand the criticism. There is however one thing about "Non-cuts are confusing" I'd like to clarify:
In this example:
foo(A, B) :-
\+ (A = B),
A = 1,
B = 2.
It's very obvious why it fails and it has nothing to do with non-cut. Let's say A can be apple and B can be orange and now you're asking Prolog to compare apples to oranges! ;)In short one has to "hint" Prolog what A and B can be so then it can "figure out" whethever comparison can be made and what is its result. Assuming there exist is_number(X) clause that can instantiate X as a number following would work just fine:
foo(A, B) :-
is_number(A),
is_number(B),
\+ (A = B),
A = 1,
B = 2.
(note that this would be stupid and very slow clause. Instantiation in such clauses like is_number(X) usually starts with some defined bounds. For A = 10000, B = 10001 and lower bound of 1 pessimistic case this clause would require 100M checks!> ISO "strings" are just atoms or lists of single-character atoms (or lists of integer character codes) [...]. Code written with strings in SWI-Prolog will not work in [other] Prolog.
That's because SWI isn't following ISO (and even moving away from ISO in other places eg. [1]).
ISO Prolog strings are lists of character codes period. It's just that there are convenient string manipulation-like predicates operating on atom names such as sub_atom, atom_concat, atom_length, etc ([2]). You'd use atom_codes to converse between atoms/strings or use appropriate list predicates.
[1]: https://www.reddit.com/r/prolog/comments/1089peh/can_someone...
[2]: https://quantumprolog.sgml.net/docs/libreference.html#string...
The reason Prolog doesn't support trailing commas is exactly the same reason JSON doesn't support trailing commas: the language is designed to be parsed by an operator precedence parser, and the comma is an infix operator.
The fact that Prolog is a purely operator precedence syntax, with a dynamic operator table, allows you to build DSLs embedded in Prolog. CLP(FD) uses this to create the #= operator.
And once you've settled on one of these, which learning resource should one go with?
It's perfectly fine to not like Prolog, but I do feel that if you're going to write an article about why you don't like it, you should at least spend some time figuring it out first.
He says of the cut operator "This is necessary for optimization but can lead to invalid programs." Imagine if a programmer new to C++ said the same thing of the "break" keyword. That's how ridiculous it sounds. Yes, cut can be used to prune backtracking and eliminate unneeded work, but that's hardly it's purpose. It leads to "invalid" programs (by which I assume he means, programs that do something other than what he wants) only in cases where you are using it wrong. Cut is no more "necessary for optimization" than break is. It's a control structure that you don't understand
Negation (\+) is confusing, and the author correctly provides examples where its meaning is unintuitive when applied to unbound variables. That's because it's not strictly speaking a negation predicate, but rather a "not provable" predicate. In that light, the examples in the article make perfect sense. Yes, Prolog is a programming language, so the order of terms matter, even if the order wouldn't matter in pure logic.
Look, Prolog is a weird language. It has a learning curve. It's not "just another language" in the Java, C++, Pascal, Python mold. I get it. But this article has the flavor of an impatient newbie getting frustrated because he can't be bothered to read the documentation.
| ?- findall(A, (tree(A, N), branch(N)), As).
As = [n,n1]
yes
See https://lpn.swi-prolog.org/lpnpage.php?pagetype=html&pageid=...Bidirectionality has always been super fascinating.
Didn’t know about Picat. 100% going to check it out.
goal :-
true
, subgoal(A, B)
, subgoal(B, C)
.
This is definitely not standard and I don't know if the WAM optimizes out the gratuitous choice point, but it certainly makes the code easier to work with.You can't do length + 1 as a single expression in any language though, in python for example len(ls) + 1 is two expressions, but I get what you mean, it is a little less terse. But those commas in prolog are super useful. And you can make your example bidirectional too:
``` :- use_module(library(clpfd)).
foo(List, X) :- length(List, Out), X #= Out + 1. ```
``` ?- foo(X, 3). X = [_, _] .
?- foo(X, 3), X = [a,b]. X = [a, b] . ```
-----
No standarized collection types:
I think that's a feature rather than a bug. The ability to just use infix and compound terms in a generic way without it being tied to a clause or operator is a huge feature. For example:
``` pets(dog-rex-poodle, dog-fluffy-bishon). ```
That defines the collection in a semantic or mathematical sort of way, then you can pass that into whatever data structure you want, whether it's a ordered map or a hashmap or whatever.
----
No boolean values:
That's also a feature in prolog, same as having no null. It's the same sort of motivation for Haskell's Maybe and Either types or Rust's Option and Result types, same kind of usefulness.
----
Cuts are confusing:
Conditionals don't need to use cuts, the modern prolog way of doing it is using the reif library:
``` :- use_module(library(reif)).
if_(Cond, Then, Else) ```
For example:
``` :- use_module(library(clpfd)). :- use_module(library(reif)).
sign(X, S) :- if_(X #> 0, S = pos, S = nonpos). ```
---
Non-cuts are confusing:
This isn't doing what you're thinking:
``` \+ (A = B) ```
It's unifying A with B, `A = B` unifies. You want `dif(A, B)`.
----
Straying outside of default queries is confusing:
It might be tempting to use bagof, but it's not monotonic. You should try to write as much prolog code as you can in a pure, monotonic way, so your code can take advantage of prolog's unique advantages, like monotonic debugging.
Check out the SWI prolog discourse group, there's a lot of good stuff in there and everyone's very supportive to newbies. And Markus Triska's website and youtube channel Power of Prolog is a super useful resource for all this stuff, a good place to go if you want to know what sets prolog apart. A lot of prolog's usefulness doesn't show up as an embedded language either, like minikanren isn't "pretty much prolog", it lacks a lot of stuff that prolog offers. Multiparadigm programming languages are also really popular now, but a lot of what sets programming languages apart isn't what they can do, it's what they can't do, what sort of paradigm or way of thinking they force you to adopt.
Picat (mentioned by the author) Datalog Mercury XSB
are there more?
IMO it would be better to have something like Prolog as part of a "better designed" language per se. I can't come up with a good proposal myself - language design is hard, including syntax design - but imagine if Prolog would be a part of python. That feature would then be used by more people. (This is just an example; just randomly creeping in features into a more successful language, also often won't work. I am just giving this as an example that MIGHT be better.)
In that case I won't try to correct any of the author's misconceptions, but I'll advise anyone reading the article to not take anything the author says seriously because they are seriously confused and have no idea what they're talking about.
Sorry to be harsh, but it seems to me the author is trying their damnedest best to misunderstand everything ever written about Prolog, and to instead apply entirely the wrong abstractions to it. I don't want to go into the weeds, since the author doesn't seem ready to appreciate that, but Prolog isn't Python, or Java, or even Picat, and to say e.g. that Prolog predicates "return true or false" is a strong hint that the author failed to read any of the many textbooks on Prolog programming, because they all make sure to drill into you the fact that Prolog predicates don't "return" anything because they're not functions. And btw, Prolog does have functions, but like I say, not going into the weeds.
Just stay away. Very misinformed article.