Prolog's Death (2010)
synthese.wordpress.com
synthese.wordpress.com
I agree that "easier to understand" could've helped. But things that are hard to understand can get uptake if they're big wins. The bigger problem in my opinion is that the number of cases where Prolog was a big win significantly decreased over time. When it appeared in the '70s, Prolog's declarative-programming approach based on logic had very few peers where you could do even simple textbook examples in as nice a way. But now even SQL (with features like recursive queries) can do a lot of the intro-level Prolog examples. It doesn't have the full logic semantics with unification, but a lot of problems don't need them. SMT solvers, LINQ, and rules engines like Jess/Drools are a few other declarative paradigms that ended up eating into some of what Prolog proponents once saw as its space. If Prolog were up exclusively against FORTRAN77 or K&R C, there would be many problems where it's a big win, but that's not the competition anymore.
Prolog's lack of popularity suggests that viewing a computer program as a pure (first order predicate) logic construct isn't a powerful way of thinking in general. That is a bit of a blow to all the programmers who seem to secretly want to be mathematicians because that in turn suggests that the logical aspects of programming are subordinate to hardware realities.
I've gotten a lot of joy out of the Neural Networking fad similarly eclipsing the logic-based AI people. Logic is important and it isn't going away, but reality has too much uncertainty for simple logic to work in practice. The statistically grounded approach makes me happier, and again computer hardware's power is overwhelming the efforts of the logicians to tie everything down to certainties.
Great language though, everyone should take a look at it to see what a different programming model might be.
http://incompleteideas.net/IncIdeas/BitterLesson.html takes this even further, suggesting that even the statistically grounded, human-enriched approaches are loosing way to simpler methods fuel by hardware advances.
1. In about 12 years we will have another "tick" of general purpose compute doubling. This will happen as sensible, known architectural changes and the slowed progress of Moores type hardware development come together.
2. We will have a wave of hetrogenous and specialist hardware architectures that will bump things along.
3. The hardware manufactures will crack and license / sell servers on a core/hour basis. This will allow people to burst compute on 100's or 1000's of cores without the always on capital commitment. This won't impact on the cloud providers but will provide on prem with a lease of life and allow people who can't migrate to the cloud due to legacy etc an escape hatch.
After that, I see a real choke on compute progress; expect a doubling every 50 years at best. The current "Moores" rate is 20 years, but that's based on the current investment fat industry. Once investors get there heads around the technology realities I expect all the cash to come out of chip making really quick. Innovation will crash stop.
At that point it's going to be software or bust for AI. I predict software...
No... it doesn't suggest that. If there's one thing I've learned all these years I've learned about various different softwares and languages, it's that popularity does not correlate with power.
I'll take Linux over Windows, Archlinux over Ubuntu, Haskell over Java, i3 over Gnome, CLI over GUI, vim and shell over IDEs like Eclipse. I've seen "modern" popular languages adopt features from "obsolete" unpopular languages in ways that pale with the originals, like Python's lambdas and assignment unpacking, which suck.
The rest of your post notwithstanding, I disagree with the logic of your first sentence.
Off the top of my head, things you can't do:
# nested unpacking
(a, b), c = ((1, 2), 3)
# destructuring arguments
(lambda (a, b): None)((1, 2)) (lambda x, y: None)(*(1, 2))
Unless you're saying you should be able to define the set size you pass in.* You can't nest, like it was mentioned
((x1, y1), (x2, y2)) = makeLine ...
* You can't use aliases line@(point1@(x1, y1), point2@(x2, y2)) = makeLine ...
* You can't use it on all objects/structures/datatypes Line (Point x1 y1) (Point x2 y2) = makeLine ...
* You can't pattern match on it case nextShape of
Circle centerPoint radius -> ...
Square width height -> ...
Triangle point1 point2 point3 -> ...
* You can't use it on functions/lambdas \ Line (point1@(Point x1 y1) _) -> ...
drawLine (Line (point1@(Point x1 y1) _)) = ...
* The syntax in Python 2 doesn't support unpacking the first few elements of a list of variable length. Python 3 added support, though. x1:x2:xs = ...> * You can't nest, like it was mentioned
((x1, y1), (x2, y2)) = makeLine ...
This works fine in Python: ((x1,y1), (x2,y2)) = ((1,2),(3,4))
I don't know what "makeLine ..." returns, but if it doesn't exactly match the tuples on the left hand side, it won't fly in Haskell either.> * You can't use aliases
line@(point1@(x1, y1), point2@(x2, y2)) = makeLine ...
No, but this works: line = p1, p2 = ((x1,y1), (x2,y2)) = ((1,2), (3,4))
and is more readable IMHO.Point 3, 4 and 5 don't make sense in Python. This is pattern matching, not tuple/list unpacking. (One could argue that tuple unpacking is a form of pattern matching, but that is a different story...)
A language could be very powerful in your sense, but abstract away from some important aspect of computation. (For example, Haskell is very powerful, but the space complexity of a Haskell program is hard to determine from the language spec.) Then such a language wouldn't be a "powerful way of thinking" in the sense of grandparent - in thinking about some aspects of computation it would hinder rather than help. The same is true for Prolog, I think.
Of course it's powerful. The problem is that it's far removed from, and in many cases in conflict with, how computers actually perform computation.
Lisp and its variants (especially Clojure) enjoy increasing popularity for all sorts of general-purpose use. Lisp is merely a particular notation for expressing lambda calculus, and is rather far removed from the realities of Von Neumann hardware.
I'd argue Prolog's demise is due to three facts: (a) the sorts of ideas best expressed in Prolog have diminished due to new languages becoming available, (b) the remaining ideas best expressed in Prolog are only applicable to a narrow set of problems, and (c) Prolog itself isn't the most ergonomic language to use, so it isn't often people's first choice when alternatives are available.
Perhaps someone who uses Prolog regularly can chime in.
(As an aside, in my experience, people who believe that "programming == math" seems to be mostly attracted to languages like Haskell and OCaml, not so much to Prolog.)
Agreed, but note Lisp also is very math-oriented (it’s a syntax for lambda calculus).
Lots of that could be implemented via lambdas and recursion, but it could also be implemented by any other Turing-complete system.
Why do you think it suggests this?
Prolog is really a database query language. Similar to SQL or GraphQL. Prolog failed because it never got integrated into a serious enterprise data storage engine. (Most Prolog implementations are just text files and in-memory hash tables.)
Actually, Prolog's clause selection rule that relies on clause ordering in the database is a boon when it comes to understanding backtracking during debugging ("tracing", please). You know that if you have two clauses of the predicate p/2:
p(a).
p(b).
And you make the query: ?- p(A).
The interpreter will first find the result p(a) and then backtrack to p(b). You know the order in which choice points will be created, that is. This makes it infinitely easier to trace a Prolog program than in a hypothetical (and very impractical) "purely" declarative langauge where clause order wouldn't matter.Now, tracing a complex program with lots of recursive calls- that can be difficult. But that's not because of backtracking. It's because of the way Prolog "unfolds" recursion, which is something I'd have trouble explaining even after ten ish years of coding in Prolog. It's something you have to develop a feeling for, after tracing a sufficient number of recursive programs. Now _that_ I'd agree is a difficulty that may keep programmers from using the language. But- backtracking? I don't agree.
http://www.swi-prolog.org/pldoc/man?section=debugger
here's an example of a trace from a CLI section.
[trace] ?- animal(X). Call: (7) animal(_G1588) ? creep Call: (8) is_true('has fur') ? creep ^ Call: (9) format("~w?\n", ['has fur']) ? creep has fur? ^ Exit: (9) format("~w?\n", ['has fur']) ? creep Call: (9) read(yes) ? creep |:
Most don't understand the standard 4-port prolog (call, exit, redo, and fail.) making it hard to grok what's going on.
http://www.swi-prolog.org/pldoc/doc_for?object=section(2,%27...)
The four-port debugger takes some explaining, but it's not the end of the world. It's actually a very conceptually simple way to understand Prolog's execution model. It's a shame that it's not taught more often.
I know this sounds counter-intuitive, but if you've been following such trends for a while, you might agree with me.
The hype goes on waves, "symbolic AI solves everything!", "no, numeric AI solves everything!".
The thing with "Datalog" is that it is really a level of functionality that is implemented in various database query systems and not a well-defined language in and of itself. 15 years ago I remembered searching for papers about it and did not find so many, now it is hot.
The painful thing about Prolog, I think, is the mashup of declarative and imperative, it just doesn't come across as natural.
Datalog as a language is really just one very specific form of rules (first-order horn implications containing just constants and variables, where each variable in the conclusion also occurs in the premise), and every Datalog program (i.e., every set of rules) is guaranteed to have a finite, universal model.
[0] Ceri, Gottlob, Tanca. (1989) What you always wanted to know about Datalog (and never dared to ask). IEEE TRANSACTIONS KNOWLEDGE AND DATA ENGINEERING. [1] Abiteboul, Hull, Vianu. (1994) Foundations of Databases: The Logical Level. Pearson.
Clarification: I dont actually think prolog is terrible (I credit it as the most exciting language I have ever learned), I just mean its not intuitive to "program" with in the imperative sense of telling a computer what to do. What I mean by it is excellent as a query language is - given a set of data, it is great for drawing conclusions from that data (but not in the same way as a "traditional" query language like SQL).
[[?person :name “Bob”]
[?person :state :tx]]
To query for “the person named Bob who lives in Texas”$x has name 'Bob'; ($x, $y) isa lives; $y has name 'tx';
On the other hand, Prolog programs are logic theories (as are Prolog queries) and their executio is a proof. The range of programs that can be expressed in Prolog is the set of programs that are computable by a Turing machine. So yes, Prolog is a programming language. Whether it's "terrible" or not is up to personal taste.
I mean, I don't konw of an objective measure of what makes a programming language "terrible".
There is probably a very human reason for that, but mathematically both representations are perfectly replaceable.
https://www.mercurylang.org/If you're used to C/C++/Java, then SQL or Prolog seems frustrating, unnatural and non-intuitive. But once you have the eureka moment and realize they are declarative, you appreciate the elegance and power of SQL or Prolog.
If you don't understand the abstractions upon which you're building, you'll have trouble building upon them.
Unification and backtracking take some effort to grok. If you grok them you can put them to use in a clean and efficient way. If not then Prolog remains a mystery.
I mean, people use SQL just fine without understanding how the DB is going to accomplish their queries.
Maybe Prolog just doesn't have the same level of tooling as SQL for deducing "what's going to happen", e.g. an equivalent to SQL's `EXPLAIN ANALYZE`?
> The traversal of a search space in which choice-points are introduced whenever multiple clauses match the current computational goal and a process of (possibly partial) variable instantiation ... and worse of all, the order in which you wrote your clauses in the program makes a difference to how it gets executed and, indeed, whether any part of the program is reachable.
Some people (more than use Prolog) use Erlang—even the parts like chained binary pattern-matching†—just fine. And some people (still more than use Prolog) use the MLs just fine, too, including functional combinators and passing around monadic bindings, despite this playing utter hell on determining "whether any part of the program is reachable."
† `foo(<<A/32,B/A,Rest/binary>>)` — an Erlang clause-head which takes a binary string, and attempts to unify the variable A with the first four bytes of it, and B with the next variable-A bytes of it, and Rest with, well, the rest of it. I.e. A is taken as a uint32 and used to calculate the bounds of a slice on B, all during the attempt to pick a clause to execute. This is common, idiomatic code.
> That this process of computation is difficult to grok is especially noticable when you try to debug a Prolog program. Computations get undone when attempts at satisfying a goal fail...
People write Solidity code for the Ethereum VM just fine. (In fact, this one is kind of hilarious; Prolog is less popular than even an arcane programming environment like the EVM—where all function calls are implicitly nested MVCC transactions that roll back any side-effects upon their Turing-machine substrate upon any trap or fault, including even rolling back the emission of logging statements and the reservation or nullification of memory.)
This might be a big part of it. SQL is a limited domain, with limited tools available. Doing "general purpose" programming with SQL requires advanced trickery and is implementation-dependent.
Another example might be Excel, which has a declarative interface (i.e. a GUI) and a semi-declarative formula syntax.
† https://learnyousomeerlang.com/syntax-in-functions#guards-gu...
‡ `length(List)` is allowed in guards and is O(N), though it could be O(1) in a different VM implementation that makes different trade-offs.
f(<<Length:32, Packet:Length/binary, Rest/binary>>) ->
That's because the algorithm that prolog uses to do unification uses committed choice - if the logic could be run using efficiently grounded answer sets then the behaviour could be made consistent and that would make the semantics a whole lot clearer. Especially if ! was done away with as well.