Prolog is awesome and I recommend learning it if only for kicks and experiencing foreign programming paradigm. Many concepts are expressed in a completely different and (when paradigm "kicks in") are arguably much simpler.
E.g. in Prolog you don't usually operate on collection but instead provide information what the collection item actually is and then let the implementation figure it out. Drawing dependency tree is an effect of 3 rules: what is an entity, how to figure out that entity X and Y have relation, how to represent it.
I have 2 gripes with it, though. One is that it's difficult and thus it's hard to convince people to learn/use it. Second - I didn't yet found way to seamlessly integrate it into other apps (I know that FFI is an option, but it doesn't convince me as production ready).
For the last couple months I've been looking for some more "production" Prolog alternatives but what I found so far (LispWorks, Franz Allegro CL) costs a kidney and is not integration friendly.
If anyone has success integrating Prolog in modern software please share, I'm really curious.
I'm also looking for some GUI solutions in it as XPCE doesn't work on Mac :(
I've worked a couple of years developing supply chain optimization software in C++ using ILOG constraint propagation software (Solver, now owned by IBM). At the time we considered BinProlog. It had built range propagation, compiled to native x86 and had descent FFI interop.
Nevertheless seems to have been abandoned in favour of SWI prolog and GNU toolchain.
Not sure about GUIs; I would consider that a very different expertise. We used our own canvas C++ rendering engine and some horrible integrated 4GL devtool call "4D", or "Fourth Dimension".
I spent a year while failing to get a PhD in process scheduling writing all the constraints in Prolog, before the university got an ILOG license and I realised how much time I'd wasted.
> Prolog is awesome and I recommend learning it if only for kicks and experiencing foreign programming paradigm. Many concepts are expressed in a completely different and (when paradigm "kicks in") are arguably much simpler.
Seconded. Probably the biggest mind-explosion type experience as a programmer in my entire life. Even something simple like building a Sudoku solver is insanely fun IMO.
I agree. Prolog was the only time I've been very much mindblown when learning programming languages. Erlang/Elixir is a close follow-up, but that was more of the seamless nature of it all and not really the ideas like Prolog.
You may be interested in Shen. It has this KLambda kernel language that can be implemented by a host platform, which then gives you the entire Shen language that includes a Prolog. I recently learned about this.
> If anyone has success integrating Prolog in modern software please share, I'm really curious.
Not sure if these count but:
- Gerrit's submit predicates are configured in Prolog. (Edit: deprecated!)
- An internal product I worked with at Google is built around a Datalog knowledge base that gets codegen'd. Then you can query it directly or use a UI to generate queries.
Both of these seem to work nicely but they're pretty limited use cases.
My experience with Prolog at university was that it was pretty fun but there's just no way I would wanna use it for anything that isn't inherently Prolog-shaped. As soon as we were implementing nontrivial stuff in it the abstraction leaked like a sieve and you felt mostly like you were hacking against the backtracking algorithm rather than taking any actual advantage of the language.
Yeah, Gerrit predicates being deprecated are (in my opinion) sign how hard it is to convince people to use Prolog. If people maintaining Gerrit cannot use it (and probably there was push at Google too), then how to convince The Median Dev...?
Not sure what "push at Google" means. For full context: the idea to use Prolog came from the project founder. In retrospect, it was a terrible idea, because almost nobody knows or likes Prolog, and that includes the SWEs at Google both on my team, and the ones configuring Gerrit.
I'd say "we don't have the human resources to support the development of this system in the long term" to be a pretty valid technical reason. Remember that we're engineers, not scientists -- the goal is efficacy
Submit blocking is not only a feature targeted at admins (they configure how submit criteria), but also for users: users have to know if their change is ready to be submitted.
IIRC, The result of a failing Prolog rule was a single string, which would surfaced to the user. It was often impossible for users to understand why that condition triggered and what they could do to address the issue.
On a technical level the idea was also flawed. The Prolog interpreter had to be constrained (to avoid infinite loops), with hardcoded recursion limit. This meant that adding one Prolog rule too many could trigger a failure in the interpreter which would then throw 5xx errors and wedge the server.
>> The Prolog interpreter had to be constrained (to avoid infinite loops), with hardcoded recursion limit.
Oh well that sounds like bad coding, definitely. You may run into that sort of problem -infinite recursions- the first few times you code in Prolog but you learn to avoid it, just as you learn to avoid infinite do-while loops etc.
Btw, since you mention the server being wedged, a few years ago I was working at a C# shop and there was an incident where _all_ our deployments died because of an infinite iteration. Some code was missing its exit condition from the loop and since this was on our platform which had the common functionality, it affected all our clients.
There was a meeting upstairs, inevitably. When the development team head returned from the meeting he told us that he had explained what happened and the upstairs had decided that we should no longer use iteration because it risked going haywire, as we had just seen. The alternative? Use recursion instead. In C#. Obviously nobody ever complied with this stupid idea.
But you can bring down a bunch of servers very easily in any language you like. Another time, in the same company, there was an infinite recursion now, created in the web templating language we used, an in-house version of django. That was a bit unexpected. In another job, I got some gentle ribbing from colleagues for sucking up all the juice from the database servers with an inexpertly written SQL script. And so on.
I mean, people say sometimes that all that stuff about the halting problem is purely theoretical and so on, but then non-termination bites you in the boogies and you're left with egg on your practically-minded, results-oriented face.
> You may run into that sort of problem -infinite recursions- the first few times you code in Prolog but you learn to avoid it
But almost all of the people configuring Gerrit submit predicates are doing so as their first and only Prolog experience. They are not the authors of Gerrit or even the owners of the server instance.
In that case the idea was not flawed "at the technical level", but at the personal level. Like I say in another comment, a good engineer never blames her tools- especially so when PEBKAC, yes?
To make yet another analogy, I know almost no C++. If you asked me to write some C++ templates to configure your system, I'd probably make a very big mess. Despite the people saying that they're a bit like unification and so on. I just don't know how to use them. So don't ask me to. And don't ask your users who don't know Prolog to use it. That's a bad project management decision.
Also, friendly advice: configuration predicates in Prolog should _never_ _ever_ _ever_ be "rules"; only "facts". That way you don't risk infinite recursions- or run much risk besides. The same goes for any other language I think: don't let the configuration execute arbitrary code or you're in for a surprise, inevitably.
What I mean by it: Someone advocated for the solution (it ended up part of a product) and since now it's deprecated those advocacy efforts weren't successful enough to induce adoption. By your words there seemed to be only a single person, but from outsider's viewpoint its impossible to say how big push there was.
"Nobody knows or likes Prolog" is exactly the reason why I refrain from using Prolog in professional environment. I can use it, I like it, but unless I have full team of people committed it's an empty effort (even harmful).
So yeah, if smart people failed to convince other smart people, there's something wrong with it.
And in my view is people don't get to like it because current sources aren't good enough. There is documentation but it's not clear what the target solution scope is (and rarely people professionally implement Sudoku solvers), what the benefits are and what are the success stories.
>> So yeah, if smart people failed to convince other smart people, there's something wrong with it.
Maybe those weren't very smart people, after all? I mean we only have Google's assurances to go by, that they are the place where brilliant people go to feel mediocre etc, but doesn't that sound like a pick-up line, that would put off most people who don't consider themselves dumb?
So what is the replacement for Prolog as language for commit rules in Gerrit? Some Java DSL such as Drools, seeing as Gerrit went from Python to Java (and supposedly from gwt to other toolkits since)? Or some ad-hoc scripting for whatever language is used by the codebase managed in Gerrit (such as idk checkstyle for a Java-based project?). The latter case would be kindof understandable since templates/common configs for checkstyle (and alternative systems) are very common. But I'd imagine a Java parser in Drools would be infeasible compared to Prolog, and in any case absolutely unmaintainable. Maybe the problem was that the Java parser wasn't updated for current Java versions? I don't know, since nobody is using Gerrit for self-hosted git management compared to gitlab, bitbucket, and co. Without context, the argument "nobody knows Prolog" is pretty lame tbh, especially coming from Googlers.
I've coded in Prolog since 2008-9, mostly for fun until 2017 when I started my PhD in Inductive Logic Programming (basically, machine learning of Prolog programs). I've coded almost exclusively in Prolog since then [1]. I've even used it professionally in two jobs in the past year, although I left both early, and now I'm doing a post-doc in explainable machine learning for robotics, again with Prolog (ILP, really).
All this to say that I've worked on a few substantial, non-trivial software projects in Prolog and I can say with confidence that "the abstraction" does not "leak like a sieve", like you say. It's bad programming practices that leak like a sieve. You can get in a real mess in Prolog if you are not careful. But you can get in a real mess with OOP if you're not careful, as e.g. in the object-relational impedance mismatch.
One of the projects I worked on in one of the two jobs I mention above was indeed a complete mess. But not because of Prolog: because of people who copy/pasted code left-and-right and puked spaghetti on the source file completely oblivious of the need to, one day, maintain their mess. Again, nothing that Prolog is particularly guilty of.
Remember: programming languages don't kill projects; programmers kill projects.
_________
[1] I've become like the proverbial FORTRAN programmer: when I tried coding in Python it came out all Prolog-like. Bad idea. Python doesn't have tail-call optimisation by default.
It goes over some style matters but also makes more practical recommendations about using the language for best results. I have followed those guidelines in the last few years, often before reading the book, and they have generally served me well.
My own, very personal advice is to avoid the dynamic database as if it was the plague. To clarify, the "dynamic database" refers to the use of assert/1, retract/1 and friends to add to, and remove clauses from the program database. Prolog programs are stored in a relational database and since we can open it up, write new program clauses to it, and compile it again, we call it the "dynamic database". Well, it may be dynamic, but it is also evil. Every time I use it, I end up hurting myself. So I've learned to hurt myself before I ever use it, just because that saves me most of the later pain.
In my current project I found myself going to great lengths to avoid using the dynamic database. For example, I have some code that auto-generates predicates to access the elements of a list that represents the state of the world as perceived by an autonomous agent guiding a robot. These "access predicates" look like this:
inspect(map_area,map_area(X_min,X_max,Y_min,Y_max),[map_area(X_min,X_max,Y_min,Y_max),_,_,_,_,_]).
inspect(scan_area,scan_area(X_min,X_max,Y_min,Y_max),[_,scan_area(X_min,X_max,Y_min,Y_max),_,_,_,_]).
inspect(position,position(X,Y,Theta),[_,_,position(X,Y,Theta),_,_,_]).
inspect(observations,observations(Contacts,Count),[_,_,_,_,observations(Contacts,Count),_]).
inspect(grouped_observations,grouped_observations(Groups,Count),[_,_,_,_,_,grouped_observations(Groups,Count)]).
% etc.
Here, the list that represents the state-vector is the last argument of inspect/3 and its terms can be inspected by passing an initialised state-vector list to that argument. For example:
?- initialised_state_vector(_Vs), inspect(position,P,_Vs).
P = position(x(0.0), y(0.0), theta(0.0)).
Modification operations work in a similar way:
modify(position,increment,x,[Map_area,Scan_area,position(X,Y,Theta),Scan_params,Observations,Grouped_observations],
[Map_area,Scan_area,position(X_,Y,Theta),Scan_params,Observations,Grouped_observations]) :-
increment_float(X,X_).
modify(position,decrement,x,[Map_area,Scan_area,position(X,Y,Theta),Scan_params,Observations,Grouped_observations],
[Map_area,Scan_area,position(X_,Y,Theta),Scan_params,Observations,Grouped_observations]) :-
decrement_float(X,X_).
% etc.
All this is to avoid writing the terms in the state-vector (like position(X,Y,Theta)) to the dynamic database, then using asserts and retracts to modify them, which is the obvious thing to do. What I did above is also a more efficient way to access the elements of the state-vector list by unification, instead of having to scan the list to find a term (which can add up to a lot of scanning in the project I'm working on). Using asserts is also costly (but clauses can be accessed fast once asserted).
The hitch is that some of the terms in the state-vector above have arguments that are themselves lists and those lists need to be accessed to inspect or modify them. Which means scanning a list again. So I added clauses of inspect predicates that look like this:
I adore the language (with all its good and bad parts) and I always admire people who work with it professionally and try to learn from them since my professional career will not allow me to get any real world experience with Prolog. You helped with that.
I'm glad I did! But never say never. I worked in the industry with Java, C# and SQL and Python for six years before I did my PhD and started working exclusively in Prolog (almost - I do a bit of plotting in R on the side). Even outside of academia, there are opportunities to work with Prolog- though you will probably have to try harder than usual to find them, and maybe have to make those opportunities yourself. I encourage you to try if that's what you want.
When working in Java a couple of years ago I once ran into a random Prolog error. Turns out that the XML parser used by a large subset of the Java world is written in Prolog
Nope, that's a misunderstanding on your part I'm afraid. The "prolog" in that error message/SO question refers to an XML prolog, which is the part in an SGML/XML document up to the first content element ie. an XML declaration and a DTD, both optional.
I had to do a little bit of Prolog back in University, decades ago, and it broke my head.
I managed to actually solve all the programming tasks we were given (and as I was the only one to do so among my peers, many copied my solutions) - but if anyone then asked me to explain how I did it, I was at a loss. I basically stared at the screen until some sort of intuition hit, and if that didn't work, repeat the process until it does...
I guess the paradigm never really "kicked in" for me ;)
Can I suggest adding it to a "when I'm REALLY bored" list? ;-)
It took me years for Prolog to kick in and it did when I was trying to... optimize my taxes in a very complex context (e.g. where I live there's a VAT difference on ketchup depending if it's sold on the fresh hot-dog or wrapped one).
But since then I try to advocate and use it as often as I can. Mostly because paradigm is super fun to work with and refreshing after all day of working on "normal" languages.
While there are solutions based on foreign function interfaces and host calling conventions for particular Prologs, you'd basically integrate the kind of apps envisioned/demoed on the Quantum Prolog site [1] like you would any other modern subsystem - as a SaaS and/or via containers.
Let's say you have a Prolog optimization app for your business domain, such as an elaborate version of the container ship scheduling problem discussed on the site [2]. Traditionally you'd generate Prolog facts encoding the problem instance, and then invoke Prolog to solve the problem and output one or more found solutions. A variant of this expecting a certain directory structure with facts/rules to exist is used by the Aleph machine learning package also demoed on the site [3]. This is popular and very flexible because of Prolog's ultra-flexible term datatypes.
Alternatively, you can implement, in Prolog itself, a small wrapper to parse a JSON representation of your problem instance, where "implement" doesn't really describe it since basic JSON parsing using Prolog's built-in operator precedence parser is as easy as
but applied to standard input using term reading, provided your Prolog has curly braces (should come with ISO DCGs). The in-browser variant of Quantum Prolog [4] emits JSON right away.
An API/language binding to call Prolog from a particular host environment is less suited for the kind of apps discussed on the Quantum Prolog site since these tend to be longer-running, and potentially parallelized workloads. But let's say a multiplayer game universe represented as Prolog database (or maybe a highly interactive solution viewer/picker under very particular circumstances) might need a different integration model.
> I didn't yet found way to seamlessly integrate it into other apps (I know that FFI is an option, but it doesn't convince me as production ready).
There is indeed an issue with side-effects that doesn't work well with the backtracking semantics. And I am not sure there's a good solution for it. Other LP languages, like Curry, just forbid side-effects under nondeterminism and you have to collect and handle them using an implementation of effect-handlers if they aren't at the top level. That's a quite restrictive solution that needs a lot of discipline, a good architecture, and framework support.
But Prolog, doing side-effects whenever, wherever such a side-effect-entailing predicate is encountered as a proof goal doesn't work either and you're bound to make your life very difficult. Even if you are careful, the code that you produce, just looks like imperative code. So nothing really gained here from using Prolog.
I'm still looking/researching about solutions inbetween. Without too too much success.
Maybe you can review Quantum Prolog's optional reification of the regular Prolog database in order to be able to backtrack over assert/retract and other destructive predicates to represent actions in eg. robotic planning. Works basically like a call/1 variant where the Prolog database to evaluate against is specified as argument rather than assumed as implicit global default, with the database a freely manipulatable list term of facts or even rules. See [1].
That's a nice idea and it kind of works with the internal database. But in terms of FFI or other interactivity, you can't un-read bytes from an input stream or un-write to another process.
That makes interoperability inherently imperative.
My use case is, that you usually wouldn't un-move a robot. You want to re-plan from the current position, if an obstacle comes up. You sure as hell don't want to backtrack into an alternate plan from your original starting position.
>> My use case is, that you usually wouldn't un-move a robot. You want to re-plan from the current position, if an obstacle comes up. You sure as hell don't want to backtrack into an alternate plan from your original starting position.
That's right, but I think if you backtracked that far, the planning should simply fail.
Are we working on something similar? I'm using Inductive Logic Programming to learn robot plans. I don't know too much about planning but I reckon what we're learning are generalised plans (with an option to make them as specialised or general as needed).
The project I'm on has started only this June so I'm not yet at the point where there's need to change a plan, but I've done some thinking about it. I might be over-optimistic, but I think it's a matter of putting plans on a stack and allowing them to be carried out in LIFO order. Then, if the original plan (the first to be put on the stack) doesn't work anymore, you just backtrack to make another plan from the current position, after checking the current state of the world.
OK, I don't know if that makes any sense to you. Does it?
It's not the first time us two have this kind of discussion.
> Then, if the original plan (the first to be put on the stack) doesn't work anymore, you just backtrack to make another plan from the current position, after checking the current state of the world.
Sure. That's the replanning. But actually you could backtrack into a plan you've already got that has the same prefix as the plan you've already chosen. Presumably a different plan differs from exactly this position and avoids the obstacle that made the current plan fail at that point. You'd have to keep track of the movements you've done. But as the plans are generated lazily, both concepts ('replanning' and 'choosing one of the original plans that hasn't been "superseded by reality" yet') might actually be the same.
>> It's not the first time us two have this kind of discussion.
Oh, I think I remember us talking about ILP. Sorry, I didn't recognise your username. Without avatars on HN I find it hard to remember who I've interacted with. I don't think we had talked about robotics or planning though.
Anyway now I'll remember you :)
>> Sure. That's the replanning. But actually you could backtrack into a plan you've already got that has the same prefix as the plan you've already chosen. Presumably a different plan differs from exactly this position and avoids the obstacle that made the current plan fail at that point. You'd have to keep track of the movements you've done. But as the plans are generated lazily, both concepts ('replanning' and 'choosing one of the original plans that hasn't been "superseded by reality" yet') might actually be the same.
I think I see what you mean. There's certainly a problem with backtracking all the way to the point where a plan failed only to pick up again from that point- and probably fail again. I don't think I can reason about this from first principles. I'd have to try it out and see what works and what doesn't.
Clojure has a Prolog library written for it called core.logic. I definitely didn't know Prolog well enough to make good use of all the esoteric function names but it seemed cool.
I did and it's one of my favorite implementations actually!
Unfortunately I get this weird vibe with many Common Lisp libraries that they feel... legacy and deprecated. For sure CL isn't super popular language and it is very stable, but minimal development dynamic makes me feel insecure about the solution.
It sounds similar to Clojure libs. It might be an Illusion. Maybe those libs are done, zero to minimal need for further development! Our perception is shaped by regular bugfixes in other languages
I do think that the best way to enforce validation rules is with a logic-paradigm language executed both during compile and run times. That would technically be a dependent type system, but with a bias of allowing runtime verification, instead of trying to prove everything at compile time.
> Racklog is an embedding of Prolog-style logic programming in Racket. “Embedding” means you don’t lose Racket: You can use Prolog-style and conventional Racket code fragments alongside each other. Racklog contains the full repertoire of Prolog features, including meta-logical and second-order (“set”) predicates, leaving out only those features that could more easily and more efficiently be done with Racket subexpressions.
>Racket also has GUI, and basically everything else you may need.
How about good docs and examples, community, decent perf for small apps at least?
Racket newbie here. Had just played around with a little some years back when it was called MZ Scheme, PLT Scheme or such. There was an IDE called Dr Scheme, IIRC. I guess all that is part of Racket now.
Docs - oh boy. 90% of contributors to Racket are educators with many decades of experience teaching. The quality, depth, organization, interlinking, examples, references to papers... basically everything about Racket docs is at the level you just don't see anywhere else. Both the reference and guide are works of art. You get Realms of Racket and Beautiful Racket on top of that. Programming Languages: Application and Interpretation you'd find on the same shelf as SICP. How to Design Programs should be mandatory reading (and an antidote for the mind) for every OOP-focused dev out there (though, by the time they're "OOP-focused" they're most likely a lost cause anyway...) What's funny here is that Racket includes an OOP system, which is also described with incredible detail and thoughtfulness. The way they derive mixins from first-class anonymous classes is beautiful and enlightening. I've learned so much from docs.racket-lang.org that no other single resource can even begin to compare.
Performance: closer to Groovy, Clojure or Raku than to Python, Ruby or JS. That is, startup is not as fast, but execution after that is decent. For a few years, performance improvements were not the focus of development due to the migration from custom VM to Chez Scheme. Now Racket on Chez is the default and I think we'll see further improvements on that front (easier maintenance and better potential performance were the reasons behind the migration IIRC.)
The community is small, and the core strength of Racket is not something normal programmers use in day to day programming (that strength being the ability to shape the syntax and semantics of your code at will, ad hoc, anytime, with no friction at all, in any way you want). On the other hand, the results of giving that power to a bunch of very smart people are really worth seeing.
There is also Minikanren. While not the same as Prolog, it still allows you to do similar reasoning from inside your language. I always found the talk by Byrd and Friedman about it at RacketCon fascinating and wish I would get to use it or something like it at $job.
This is also the common pattern for using Prolog in Common Lisp. It goes back to the first generation AI days back in the 70s. Peter Norvig's Lisp AI book has a nice rundown.
> I recommend learning it if only for kicks and experiencing foreign programming paradigm. Many concepts are expressed in a completely different and (when paradigm "kicks in") are arguably much simpler.
I say the same thing about CUE these days, it can be a real mind bender but gives us a new way to think about taming config and schema complexity. CUE is also much easier to add into existing workflows to turn that logical programming glory into pragmatism.
It was originally developed at Google, but Marcel is no longer there, there is a team and company behind it now.
The language is going to a foundation (still waiting for update from the CUE team), fun fact, it started as a fork of Go (for the lexer, parser, and a bunch of goodies in that source tree)
CUE's history is from borg, borgfig, GCL, but takes the approach Marcel always wanted to if not tied to the existing practices in a large company, namely Typed Feature Structures and logical programming (no overrides, layering, inheritance, general recursion)
We're about to release a TUI for working with CUE, and hoping that it will make learning much easier for people. We do a lot with Cuetorials, but there is only so much you can do on a website (/rant WASM limitations). Using the Go SDK is the way to unlock CUE super powers.
Check out Mercury! [1] - Strongly typed and integrates with other languages really really nicely. Besides the reference material on the official site, there's a lovely crash course [2] as well.
Prince's [3] base is Mercury and integrates Rust and C without fuss.
I’ve had a few use cases for a Prolog-suitable solution over the past decade, but there doesn’t seem to be an easily embeddable Prolog system available, or maybe there wasn’t a couple years ago. I needed a small library without IPC, sockets, forking, watchdogs, etc. Python and node bindings are a gigantic bonus.
IMHO Prolog is mostly a way of thinking, of designing your software. Making a blazingly fast compiler is an art, but the basic interpreter at the heart of prolog is fairly straight forward to design yourself. Prolog isn't as old as LISP, but keep in mind it had useful applications on contemporary hardware in the 1970s.
I worked with a guy that was doing natural language analysis of some sort in Prolog for a contract (we worked at a game development studio, so this was entirely external).
I asked him how he actually used it and he basically said what you wrote here: He implemented his own interpreter (in C#) and limited backtracking considerably (as far as I understood) in order to make it useful performance-wise for their use case.
This paper is also very interesting. It describes the Janus System, a deep integration between Python and Prolog: https://arxiv.org/pdf/2308.15893v1.pdf
Curious to learn whether Prolog is still an integral part for contemporary CS (proposition, predicate) Logic curriculum, natural language parsing, etc. I suppose that the built-in backtracking was the most controversial aspect, so maybe attention has shifted to Erlang, Haskell or esoteric languages like (wild) life?
As a student, I learned Prolog and Caml in a master's in CS in France, in 2015, though the languages we were most likely to use like Java and C had much more hours of class. I think the teachers, who are also researchers, rarely update the technologies they teach because it's often what they learned when they started teaching.
That sounds very familiar. ;-) (For the similar reasons I learned Pascal, Modula 2, Ada when I ought to have learned about C++, Java)
If you were a teacher now, having to explain about predicate logic and proof automation, would you choose teaching Prolog? If not, which language would you choose?
I work as a web developer, and rarely use exotic paradigms like Prolog's logic programming or OCaml's functional programming, so I do not know many alternatives, but I like the value of showing all of these to students, so I'm still ok with teaching Prolog to students, because I'm not familiar with alternatives.
If I knew alternatives that were very recent, like 4-5 years old, I'd be careful replacing the current courses, because often in technology, statistically, if something has been there for a long time, it'll be there for a long time, and many new things die young.
I recently did a stint as a TA for my university's undergraduate Prolog course. It's pretty much an elective here with the main goal of introducing students to a a different paradigm. Applications of Prolog for natural language were discussed but not the main focus. I reckon that the FP course (using Haskell) touch more about parsing (using parser combinators) than the Prolog course.
Both my B.Sc./Ma.Sc. and Phd institutions (Germany) currently teach Prolog as an elective. One does it as a Bachelor's course of symbolic AI and the other does it as a master's course in Logic Programming and Deductive Databases (together with Datalog).
AFIK all CS B.Sc./Ma.Sc. have logic (theory) in their curriculum often as a required curse, sometimes as part of other courses.
Many but not all do also have logic programming in some form. Through that might be just some side project of another course, or might not be done using Prolog.
I remember learning Prolog during my degree in AI + Comp Sci about 25 years ago. Our NLP module used it heavily.
It was frustrating and annoying at first, but once you get comfortable, surprisingly fun (and also a great intro to recursion, as that's the only way to iterate).
Similar experience. It was frustrating and annoying at first for me too, because I was trying to write imperative code in Prolog syntax. But once the new paradigm clicked in my head, OMG, I started loving it. That moment of enlightenment alone was well worth taking that class :)
One of the things Prolog taught me was the importance of teachers who can put themselves in the shoes of the student.
At university we were largely taught Prolog by mathematicians who exclusively worked in such languages. Prolog was a very natural way of thinking for them, and many of them had never really used other non-logic/functional languages. For most of the students, who had worked mostly with procedural languages before, it was confusing and frustrating, we would all get tied up trying to use Prolog as if it were some other language.
It wasn't until my third or fourth course that used Prolog, and having studied the algorithm behind it, that I really got my head around how to use it well.
Later on I was helping an undergrad I knew was having problems with Prolog. It was mostly about how to change the way he thought about solving problems to fit in the Prolog world, rather than trying to make Prolog behave like all the other languages he'd used (he was a great developer in everything except Prolog). After quite a bit of discussion there was a real epiphany and he suddenly got it and he proceeded to nail the coursework.
We had it in my comp sci bachelor's just a few years ago. But it was a small component of the AI course instead of being a big part of it. Fun stuff regardless.
I'm currently trying to learn Prolog. Reason is because I attempt to reignite my enthusiasm for IT by going back to the roots. And the joy of learning new programming languages / paradigms was what brought me into the field more than 20 years ago.
I find Prolog very challenging for several reasons. I identified 3 levels of difficulty with it:
1) Syntax
2) Reasoning and Paradigm
3) Implementation
1) is things like "\=" for not equal. Those are mostly just inconveniences and Prolog's age is showing there. But I still don't have a firm grip on the pattern matching of lists.
2) is the whole stuff around switching mentally from functions to predicates. I often find myself trapped in trying to reason functionally and if I was very proficient in functional programming that might even work. But so far I'm still at the level of trying to wrap my head around the fact that there no return values and that sequencing via comma (which is actually a conjunction - AND) will be evaluated "kind of" procedural by serial evaluation. This makes it seem familiar but conflicts with the actual paradigm.
3) is the most critiqued aspect of it. There are many Prolog implementations it but they all seem to suffer from the same problems. Like Prolog getting lost in some tree which forces the developer to understand what the interpreter is doing to help it find the right branch. Also the aforementioned confusion that logically (A AND B) is identical to (B AND A) but in Prolog isn't and can make the subtle difference between the program finishing successfully or running until memory is consumed.
In my opinion there is reason why Prolog is one of those subjects that are brought up here again and again and again but without any justification with regard to something about it being "news". It's a very fascinating paradigm and I find it very sad that it doesn't really get any attention today. Just check the list of literature about it - there's almost nothing and what there is either old or distinctively academic.
One reason clearly is the lack of convenient interfacing to other languages. Because even a Prolog genius would probably admit that there is no point in trying to implement anything IO with it.
I'm working through Learn Prolog Now. Which is a good book. Already ordered Prolog in Adventures. I could see several projects for it that would be a natural fit but I already anticipate that eventually my enthusiasm will fade at the point where I'm forced to dig into SWI as soon as I leave the toy level. SWI is also kind of weird. It seems to be the best choice but there are surprisingly trivial aspects about it that I find very unsatisfying (I can load a DB with "[filename]." but I cannot purge it before loading another DB without restarting the session ... or does somebody know how that's done?). That's basically what I observe as far as my as well as other people's experience goes with it: very promising and exciting but eventually disappointing. It shouldn't be like that.
I also believe that symbolic AI / Prolog and deep learning could very well benefit from each other.
1. Prolog is often used in things that are kept very secret. There is a relatively small set of professional and highly committed users centered on distributions like SICStus. There is a much larger set of hobbyist/academic users centered on distributions like SWI, Tau, Ciao! and so on. The feature sets of the commercial distributions gives you some idea about the sort of capabilities the hardcore users require.
2. Getting good at solving problems with Prolog is a borderline mystical experience -- or at least it is for me. Seeing the logical structure of problems, their representation through symbolic pattern matching and their solutions as search, is an insight which once seen cannot be unseen. It will condition your perception of problems forever more.
No, that's not what I meant. I just mean people not telling other people. For example:
Pyrosequencing AB: SICS developed a dispensation order generation algorithm for Pyrosequencing's sequence analysis instruments, using constraint programming with SICStus. The algorithm can be described as a compiler, which calculates an instruction sequence based on an input specification. Applications include genetics, drug discovery, microbiology, SNP and mutation analysis, forensic identification using mtDNA, pharmacogenomics, and bacterial and viral typing.
That's a public example from the SICStus website, but you could see why the example above could have remained a secret and their Prolog used along with it. SICStus attracts such cases.
Unlike usgroup I don't think I can be very encouraging. It is very hard to learn Prolog if you approach it as a programming language rather than an implementation of the Resolution-based logic programming paradigm.
I coded in Prolog for many years and I was reasonably proficient in it, but I only really understood it during my PhD when I finally had the time to study logic programming textbooks and understand where it comes from.
Unfortunately, that is simply not the amount of time that one has unless one is doing a PhD -and one in a relevant subject, as I did.
As a for instance, \=/2 is not "not equal", but "does not unify". So for example A \= B is false, because A and B are variables and unify, a \= b is true because a does not unify with b and a \= B is false again because a unifies with B (as in a = B... which is not equality, or assignment, but unification). I'm guessing that your trouble with pattern matching (i.e. unification) of lists comes from the same place. All this will inevitably and understandably sound downright weird unless you know why unification exists. But that means you need to understand Resolution, and logic programming.
Regarding (2), indeed, Prolog predicates are relations, not functions. It might help to think of the er relation between a function, and a relation. Say for example the addition function: x + y = z; or, in Polish ish notation: +(x,y) = z. If you wrote that as a relation: +(x,y,z).
Meaning you _could_ see a relation as a function with just one extra argument that holds the return value of the function. If you have trouble transposing the function-like representation of code in your head to a relational one, for Prolog, try doing it that way. Instead of trying to write functions that are replaced by their return values, write functions+return that have one extra variable for the return.
So, sort(List) = Sorted becomes sort(List,Sorted). reverse(List) = Reverse, becomes reverse(List,Reversed) and so on. Ingore for a moment the usual comment about how Prolog predicates do not have "return" variables, only terms in some relation. True, but if it's blocking you, avoid thinking about it. Until you can think about it.
Btw, the reason that Prolog predicates aren't replaced by their value, not even their truth value, is that Prolog inherits its semantics from First Order Logic (FOL), where there's also no replacement of values. In fact, FOL _has_ functions, but they, too, are not the kind that is replaced by their value. I think that kind of function is only found in programming languages and then not all of them either.
The reason for (3) also has to do with Resolution and how it is most efficiently implemented. There are alternatives, e.g. datalog, or Answer Set Programming, which are more purely logical; or Prolog with tabling, a.k.a. SLG-Resolution, which doesn't suffer from left-recursion (what you get when you put a recursive literal first in the body of a clause).
>> Because even a Prolog genius would probably admit that there is no point in trying to implement anything IO with it.
I'm no Prolog genious, so what does "anything IO" mean?
The other comments are talking about just Prolog in general, but I have to say, The Power of Prolog is a rather excellent series of tutorials. The videos are worth watching too, Markus does a great job of capturing the viewer's/reader's interest.
As someone who shared so many commenters' experiences of initial frustrations and the later 'enlightenment' with Prolog, I recommend starting with Markus's video 'Preparing Prolog' https://www.metalevel.at/prolog/videos/preparing_prolog
This helped me to appreciate Prolog as an ongoing, progressive movement in programming.
but practically there is a difference between thinks people believe are objectively true and things which they are less sure about or find quite subjective
IMHO and AFIK are like an emoji they add additional subtle context and should in no way be read literally
like emoji they do not belong in technical articles, but are normal in more short form text conversations
I found that statement to be the most informative part of the document. It made me stop and consider it as, yes, I wouldn't have believed it until it was so explicitly pointed out.
Science is full of authors supposing what other people believe and then surprising them with their findings.
"Remarkably", "perhaps unintuitively", "convention states", "a striking consequence", "a surprising result", …
You may not believe this, but here's Feynman making assumptions about what other people think:
Because atomic behavior is so unlike ordinary experience, it is very difficult to get used to, and it appears peculiar and mysterious to everyone—both to the novice and to the experienced physicist. Even the experts do not understand it the way they would like to, and it is perfectly reasonable that they should not, because all of direct, human experience and of human intuition applies to large objects.
Here's Newton using "which is an erroneous Supposition", his version of "you may not believe this":
Are not all Hypotheses erroneous, in which Light is supposed to consist in Pression or Motion, propagated through a fluid Medium? For in all these Hypotheses the Phaenomena of Light have been hitherto explain'd by supposing that they arise from new Modifications of the Rays; which is an erroneous Supposition.
As Albert Einstein wrote:
The whole of science is nothing more than a refinement of everyday
thinking. It is for this reason that the critical thinking of the physicist cannot
possibly be restricted to the examination of the concepts of his own specific
field. He cannot proceed without considering critically a much more difficult
problem, the problem of analyzing the nature of everyday thinking.
How can you do that without making assumptions about what other people think?
> "Remarkably", "perhaps unintuitively", "convention states", "a striking consequence", "a surprising result",
are very different to "You may not believe this,"
because they don't say "you" they state it in a general way which is ambiguous about weather it was e.g. "remarkable" for them or many they know
so they don't make assumption about every specific reader, instead they make assumptions about themself and/or readers in general
The examples you state all are different and in my opinion miss the point. In no case do they say "you ..." or anything close to this. It's always in a genericway. In other words "even experts will find it peculiar", "theories of this kind are erroneous", "he - as in a generic scientist - needs to consider something".
Your comment is fair but you disregard the possibility that the author is trying to avoid some attitude he has previously encountered. Given the subject matter, I think that's the likeliest explanation.
I actively use Prolog for data analysis. There is an initial phase in which you have to force yourself to do everything you can in Prolog mostly to understand how to convert a question into a symbolic problem because Prolog has terrible support for scientific computing and string manipulation. Thereafter Prolog solutions are often more elegant than the next best thing.
It is also a logical paradigm and it is worth spending the time understanding how [proof by negation] + [unification] = [logical programming]. For me it is without doubt the most mind expanding programming language I've ever learned.
Not OP, but you may find Inductive Logic Programming (ILP) interesting, ie. ML with a logic programming flavor for learning logic theories from data. While [1] focusses on porting the well-known Aleph ILP package to ISO Prolog and getting it to speed via parallelization, it might give you enough of an idea and a couple pointers where to go from there.
Working with CSV or JSON data and performing relational ad hoc queries or employing Prolog for combinatoric test/coverage data generation is also very convenient.
Prolog has very decent support for string manipulation... there are hardly any modern popular languages that offer anything like DCGs out of the box. Even though standard Prolog doesn't have regular expressions out of the box, popular implementations usually have libraries to do that.
If I had to do a lot of work with strings, and I had to choose between, say, C and Prolog, I'd definitely go with Prolog. If it was Python vs Prolog, it would depend on a task...
Prolog is a beautiful language. I've written 10s of thousands of lines of it, for mathematical reasoning, rule-based reasoning, etc.. Those are domains where it fits perfectly, and working with it is incredibly fun.
Prolog has two problems for other kinds of applications. First, it doesn't play well with libraries and other languages, because the paradigms are so different. Second, doing any sort of decent GUI is close to impossible, again, because it doesn't fit the paradigm.
I helped write the interop with Go part of GoLog. And, no, it's not really difficult to interface with other languages. Usually, from Prolog side you see the foreign interface as a predicate with all ground arguments and from the foreign sign you interact with Prolog similar to how you would interact with SQL database: you send a query, then deconstruct the query (including requesting more solutions or driving backtracking etc.)
You could say these aren't "interesting" interactions. But this is how it would work because libraries don't really exploit the useful features of Prolog. You could, in principle, write "thicker" layers of interop, where you'd probably have to aggregate multiple library functions into single predicate interface on the Prolog side, where a specific sub-routine would be called based on instantiation mode of the arguments. But this is hardly different from exposing C libraries to object-oriented languages (like Python), where you aggregate multiple C functions to act like property accessors or to act like methods of a data-structure etc.
I can basically +1 the sentiment here. Used Prolog during my three university AI classes (basically "AI A Modern Approach" front to back + Prolog programming) and it was a mind bending experience. I always have to warm up before my brain can think Prolog again. I also wrote horrible prology spaghetti code in my other language during the Prolog semesters :D
Biggest downside "back in the day" was interop with other things was tricky. Either you embedded a Prolog in a JVM or something like that (usually with less features) or you relied on some sort of C binding. My takeaway was it's great so solve certain tasks (especially CSP) but not for solving tasks with a user interface in the mix (or file system access or any miriad of other things).
"Back then" SWI was also lagging behind and we used some proprietary solution which I'm generally not a fan of when it comes to programming languages/environement (Sicstus iirc.). Last time I checked SWI has caught up with the prorietary stuff and would be my go to now though.
The Prolog exam was also the one I was most afraid of during university because it consisted of a 60 Minute live coding section (solve three tasks of increasing difficulty) followed by an oral exam. My prep experience was that often times it just takes some reasoning and sitting there before the magic moment comes and the Prolog solution pops up in my brain...not exactly what you want in a timed exam situation. Thankfully I aced it by spending 40 minutes on the easiest task and solving the other two in a matter of minutes.
The follow up course was a lot of fun (implement a game playing agent and compete against other teams in a tournament).
"The Art of Prolog" and "The Craft of Prolog" were also two of the better programming books I have read in my life.
In Prolog you can write "pure logical" programs or you can use cuts and some other mechanisms to write imperative programs in it.
The methods for writing imperative programs in Prolog are really clever but they are also awkward. (For instance "success" is often represented as a logical false and "failure" as a logical true)
I think there has been an explosion of interest in a pure logical dialect of Prolog called Datalog
when I first got interested in the semantic web I thought "OWL sucks" and thought Datalog was an interesting alternative, although it was invented in 1986 there was very little literature on it. I got dragged kicking and screaming away from the semantic web multiple times and when I got back to it again recently I found that the opinion "OWL sucks, use Datalog instead" has gotten widespread and now there are a lot of Datalog implementations and a huge literature. (Oddly, I met somebody who taught me the true meaning of OWL and how to do interesting things with it.)
If you think that materialized views, CTEs and such are a ugly and disorganized mess in SQL you might like datalog a lot. (Of course if you think triggers are a disorganized mess you might like production rules but I haven't quite seen a production rules-based database that really works.)
Not OP. He might be referring to the looping or enumeration pattern: evaluate some clause to get the first solution, “fail” to trigger backtracking and to get the next solution, etc. At the very end after printing all solutions, the program returns “No” because there are no solutions left.
> "The Art of Prolog" and "The Craft of Prolog" were also two of the better programming books I have read in my life.
IMHO, The Art of Prolog is along with Concepts, Techniques, and Models of Computer Programming, Paradigms of AI Programming, and Structure and Interpretation of Computer Programs the canon of dynamically typed programming. The Craft of Prolog is also exceptional but has some chapters that are too specific to programming in old Prolog systems and thus outdated.
Lots of Prolog ideas live in answer set programming and Datalog. The latter is fantastic to build state-of-the-art static analyzers [1].
I got my copy back in the (gasp) '80s from the neighborhood B. Dalton's or something, and it totally changed the way I thought about the roles of code and data. It's one of the handful of books that have survived all my library downsizing, along with The Tao of Programming, the bible, the dragon book...
I love Shen, but I have not taken it on. I subscribed to the professional version for half a year, and I like the amount of implementations of Shen in other PLs. The pattern matching, optional type checking, and the built-in Prolog is incredible. It's a case where the creator has been shunned by the wider community, partially due to his diplomacy, but he is brilliant, and the uptake has been niche and bleeding edge. If it had a Mozilla behind it or other, I think it would have been more universally appealing. @deech's talks, though older now, are fantastic and relevant[1,2]. But take what I have to say with a grain a salt: I am an avid APL/J/BQN fanboy ;)
(Seriously though, there are many things that could be considered successors, like Mercury or maybe even Datalog, but to a first approximation I'd say that Prolog is still timely.)
FWIW (not much, admittedly), I once wrote a morphological parser in Prolog (mostly, there was some C code for dealing with bit-level operations). About the time I finished the project, the version I was using (Arity Prolog) went out of business. The owner made it freely available, but from what I could tell that version was buggy. The parser eventually got re-written in C, and is used to this day.
Prolog seemed a good fit, as I wanted to find all possible parses of words, and the backtracking mechanism solved that. I also need to handle morphosyntactic features (like person and number), and Prolog's unification algorithm provided exactly what was needed.
We had to implement a Sudoku solver and at some point I got stuck.
I was trying to bring over my imperative programming mindset, I had written a solver in Java before and I tried to port over the Java code to Prolog, which did not work at all.
So at some point I started over, and wrote some simple Prolog code to check if the Sudoku is valid.
Then I realized that I could also run these predicates to solve the Sudoku, and I completed the solver in under 10 lines.
It was a real eureka moment, Prolog is very powerful, but criminally underused in practice, fortunately I had the opportunity to work with Datalog and OWL.
131 comments
[ 3.1 ms ] story [ 196 ms ] threadE.g. in Prolog you don't usually operate on collection but instead provide information what the collection item actually is and then let the implementation figure it out. Drawing dependency tree is an effect of 3 rules: what is an entity, how to figure out that entity X and Y have relation, how to represent it.
I have 2 gripes with it, though. One is that it's difficult and thus it's hard to convince people to learn/use it. Second - I didn't yet found way to seamlessly integrate it into other apps (I know that FFI is an option, but it doesn't convince me as production ready).
For the last couple months I've been looking for some more "production" Prolog alternatives but what I found so far (LispWorks, Franz Allegro CL) costs a kidney and is not integration friendly.
If anyone has success integrating Prolog in modern software please share, I'm really curious. I'm also looking for some GUI solutions in it as XPCE doesn't work on Mac :(
Oh, and check out the Swish for online notebook: https://swish.swi-prolog.org
Nevertheless seems to have been abandoned in favour of SWI prolog and GNU toolchain.
Not sure about GUIs; I would consider that a very different expertise. We used our own canvas C++ rendering engine and some horrible integrated 4GL devtool call "4D", or "Fourth Dimension".
https://www.tomshw.it/data/images/2/0/4/3/infor-advanced-sch...
Seconded. Probably the biggest mind-explosion type experience as a programmer in my entire life. Even something simple like building a Sudoku solver is insanely fun IMO.
Not sure if these count but:
- Gerrit's submit predicates are configured in Prolog. (Edit: deprecated!)
- An internal product I worked with at Google is built around a Datalog knowledge base that gets codegen'd. Then you can query it directly or use a UI to generate queries.
Both of these seem to work nicely but they're pretty limited use cases.
My experience with Prolog at university was that it was pretty fun but there's just no way I would wanna use it for anything that isn't inherently Prolog-shaped. As soon as we were implementing nontrivial stuff in it the abstraction leaked like a sieve and you felt mostly like you were hacking against the backtracking algorithm rather than taking any actual advantage of the language.
(I run the team that works on Gerrit at Google.)
IIRC, The result of a failing Prolog rule was a single string, which would surfaced to the user. It was often impossible for users to understand why that condition triggered and what they could do to address the issue.
On a technical level the idea was also flawed. The Prolog interpreter had to be constrained (to avoid infinite loops), with hardcoded recursion limit. This meant that adding one Prolog rule too many could trigger a failure in the interpreter which would then throw 5xx errors and wedge the server.
Oh well that sounds like bad coding, definitely. You may run into that sort of problem -infinite recursions- the first few times you code in Prolog but you learn to avoid it, just as you learn to avoid infinite do-while loops etc.
Btw, since you mention the server being wedged, a few years ago I was working at a C# shop and there was an incident where _all_ our deployments died because of an infinite iteration. Some code was missing its exit condition from the loop and since this was on our platform which had the common functionality, it affected all our clients.
There was a meeting upstairs, inevitably. When the development team head returned from the meeting he told us that he had explained what happened and the upstairs had decided that we should no longer use iteration because it risked going haywire, as we had just seen. The alternative? Use recursion instead. In C#. Obviously nobody ever complied with this stupid idea.
But you can bring down a bunch of servers very easily in any language you like. Another time, in the same company, there was an infinite recursion now, created in the web templating language we used, an in-house version of django. That was a bit unexpected. In another job, I got some gentle ribbing from colleagues for sucking up all the juice from the database servers with an inexpertly written SQL script. And so on.
I mean, people say sometimes that all that stuff about the halting problem is purely theoretical and so on, but then non-termination bites you in the boogies and you're left with egg on your practically-minded, results-oriented face.
But almost all of the people configuring Gerrit submit predicates are doing so as their first and only Prolog experience. They are not the authors of Gerrit or even the owners of the server instance.
To make yet another analogy, I know almost no C++. If you asked me to write some C++ templates to configure your system, I'd probably make a very big mess. Despite the people saying that they're a bit like unification and so on. I just don't know how to use them. So don't ask me to. And don't ask your users who don't know Prolog to use it. That's a bad project management decision.
Also, friendly advice: configuration predicates in Prolog should _never_ _ever_ _ever_ be "rules"; only "facts". That way you don't risk infinite recursions- or run much risk besides. The same goes for any other language I think: don't let the configuration execute arbitrary code or you're in for a surprise, inevitably.
"Nobody knows or likes Prolog" is exactly the reason why I refrain from using Prolog in professional environment. I can use it, I like it, but unless I have full team of people committed it's an empty effort (even harmful).
So yeah, if smart people failed to convince other smart people, there's something wrong with it.
And in my view is people don't get to like it because current sources aren't good enough. There is documentation but it's not clear what the target solution scope is (and rarely people professionally implement Sudoku solvers), what the benefits are and what are the success stories.
Maybe those weren't very smart people, after all? I mean we only have Google's assurances to go by, that they are the place where brilliant people go to feel mediocre etc, but doesn't that sound like a pick-up line, that would put off most people who don't consider themselves dumb?
Also: a good engineer never blames her tools.
All this to say that I've worked on a few substantial, non-trivial software projects in Prolog and I can say with confidence that "the abstraction" does not "leak like a sieve", like you say. It's bad programming practices that leak like a sieve. You can get in a real mess in Prolog if you are not careful. But you can get in a real mess with OOP if you're not careful, as e.g. in the object-relational impedance mismatch.
One of the projects I worked on in one of the two jobs I mention above was indeed a complete mess. But not because of Prolog: because of people who copy/pasted code left-and-right and puked spaghetti on the source file completely oblivious of the need to, one day, maintain their mess. Again, nothing that Prolog is particularly guilty of.
Remember: programming languages don't kill projects; programmers kill projects.
_________
[1] I've become like the proverbial FORTRAN programmer: when I tried coding in Python it came out all Prolog-like. Bad idea. Python doesn't have tail-call optimisation by default.
The advice in this technical report is sound:
Coding Guidelines for Prolog
https://arxiv.org/abs/0911.2899
It goes over some style matters but also makes more practical recommendations about using the language for best results. I have followed those guidelines in the last few years, often before reading the book, and they have generally served me well.
My own, very personal advice is to avoid the dynamic database as if it was the plague. To clarify, the "dynamic database" refers to the use of assert/1, retract/1 and friends to add to, and remove clauses from the program database. Prolog programs are stored in a relational database and since we can open it up, write new program clauses to it, and compile it again, we call it the "dynamic database". Well, it may be dynamic, but it is also evil. Every time I use it, I end up hurting myself. So I've learned to hurt myself before I ever use it, just because that saves me most of the later pain.
In my current project I found myself going to great lengths to avoid using the dynamic database. For example, I have some code that auto-generates predicates to access the elements of a list that represents the state of the world as perceived by an autonomous agent guiding a robot. These "access predicates" look like this:
Here, the list that represents the state-vector is the last argument of inspect/3 and its terms can be inspected by passing an initialised state-vector list to that argument. For example: Modification operations work in a similar way: And then I can do: All this is to avoid writing the terms in the state-vector (like position(X,Y,Theta)) to the dynamic database, then using asserts and retracts to modify them, which is the obvious thing to do. What I did above is also a more efficient way to access the elements of the state-vector list by unification, instead of having to scan the list to find a term (which can add up to a lot of scanning in the project I'm working on). Using asserts is also costly (but clauses can be accessed fast once asserted).The hitch is that some of the terms in the state-vector above have arguments that are themselves lists and those lists need to be accessed to inspect or modify them. Which means scanning a list again. So I added clauses of inspect predicates that look like this:
I adore the language (with all its good and bad parts) and I always admire people who work with it professionally and try to learn from them since my professional career will not allow me to get any real world experience with Prolog. You helped with that.
(Hint: the error came from an Apache lib, just like in this link: https://stackoverflow.com/questions/3030903/content-is-not-a...)
I managed to actually solve all the programming tasks we were given (and as I was the only one to do so among my peers, many copied my solutions) - but if anyone then asked me to explain how I did it, I was at a loss. I basically stared at the screen until some sort of intuition hit, and if that didn't work, repeat the process until it does...
I guess the paradigm never really "kicked in" for me ;)
It took me years for Prolog to kick in and it did when I was trying to... optimize my taxes in a very complex context (e.g. where I live there's a VAT difference on ketchup depending if it's sold on the fresh hot-dog or wrapped one).
But since then I try to advocate and use it as often as I can. Mostly because paradigm is super fun to work with and refreshing after all day of working on "normal" languages.
Then, say 5 years later, it "kicked in" and I kept wondering "why isn't this implemented in prolog?" at many, many points.
Let's say you have a Prolog optimization app for your business domain, such as an elaborate version of the container ship scheduling problem discussed on the site [2]. Traditionally you'd generate Prolog facts encoding the problem instance, and then invoke Prolog to solve the problem and output one or more found solutions. A variant of this expecting a certain directory structure with facts/rules to exist is used by the Aleph machine learning package also demoed on the site [3]. This is popular and very flexible because of Prolog's ultra-flexible term datatypes.
Alternatively, you can implement, in Prolog itself, a small wrapper to parse a JSON representation of your problem instance, where "implement" doesn't really describe it since basic JSON parsing using Prolog's built-in operator precedence parser is as easy as
but applied to standard input using term reading, provided your Prolog has curly braces (should come with ISO DCGs). The in-browser variant of Quantum Prolog [4] emits JSON right away.An API/language binding to call Prolog from a particular host environment is less suited for the kind of apps discussed on the Quantum Prolog site since these tend to be longer-running, and potentially parallelized workloads. But let's say a multiplayer game universe represented as Prolog database (or maybe a highly interactive solution viewer/picker under very particular circumstances) might need a different integration model.
[1]: https://quantumprolog.sgml.io/
[2]: https://quantumprolog.sgml.io/container-planning-demo/part1....
[3]: https://quantumprolog.sgml.io/bioinformatics-demo/part1.html
[4]: https://quantumprolog.sgml.io/browser-demo/browser-demo.html
There is indeed an issue with side-effects that doesn't work well with the backtracking semantics. And I am not sure there's a good solution for it. Other LP languages, like Curry, just forbid side-effects under nondeterminism and you have to collect and handle them using an implementation of effect-handlers if they aren't at the top level. That's a quite restrictive solution that needs a lot of discipline, a good architecture, and framework support.
But Prolog, doing side-effects whenever, wherever such a side-effect-entailing predicate is encountered as a proof goal doesn't work either and you're bound to make your life very difficult. Even if you are careful, the code that you produce, just looks like imperative code. So nothing really gained here from using Prolog.
I'm still looking/researching about solutions inbetween. Without too too much success.
[1]: https://quantumprolog.sgml.io/container-planning-demo/part2....
That makes interoperability inherently imperative.
My use case is, that you usually wouldn't un-move a robot. You want to re-plan from the current position, if an obstacle comes up. You sure as hell don't want to backtrack into an alternate plan from your original starting position.
That's right, but I think if you backtracked that far, the planning should simply fail.
Are we working on something similar? I'm using Inductive Logic Programming to learn robot plans. I don't know too much about planning but I reckon what we're learning are generalised plans (with an option to make them as specialised or general as needed).
The project I'm on has started only this June so I'm not yet at the point where there's need to change a plan, but I've done some thinking about it. I might be over-optimistic, but I think it's a matter of putting plans on a stack and allowing them to be carried out in LIFO order. Then, if the original plan (the first to be put on the stack) doesn't work anymore, you just backtrack to make another plan from the current position, after checking the current state of the world.
OK, I don't know if that makes any sense to you. Does it?
It's not the first time us two have this kind of discussion.
> Then, if the original plan (the first to be put on the stack) doesn't work anymore, you just backtrack to make another plan from the current position, after checking the current state of the world.
Sure. That's the replanning. But actually you could backtrack into a plan you've already got that has the same prefix as the plan you've already chosen. Presumably a different plan differs from exactly this position and avoids the obstacle that made the current plan fail at that point. You'd have to keep track of the movements you've done. But as the plans are generated lazily, both concepts ('replanning' and 'choosing one of the original plans that hasn't been "superseded by reality" yet') might actually be the same.
Oh, I think I remember us talking about ILP. Sorry, I didn't recognise your username. Without avatars on HN I find it hard to remember who I've interacted with. I don't think we had talked about robotics or planning though.
Anyway now I'll remember you :)
>> Sure. That's the replanning. But actually you could backtrack into a plan you've already got that has the same prefix as the plan you've already chosen. Presumably a different plan differs from exactly this position and avoids the obstacle that made the current plan fail at that point. You'd have to keep track of the movements you've done. But as the plans are generated lazily, both concepts ('replanning' and 'choosing one of the original plans that hasn't been "superseded by reality" yet') might actually be the same.
I think I see what you mean. There's certainly a problem with backtracking all the way to the point where a plan failed only to pick up again from that point- and probably fail again. I don't think I can reason about this from first principles. I'd have to try it out and see what works and what doesn't.
https://github.com/clojure/core.logic
Unfortunately I get this weird vibe with many Common Lisp libraries that they feel... legacy and deprecated. For sure CL isn't super popular language and it is very stable, but minimal development dynamic makes me feel insecure about the solution.
This doesn't directly answer you, but Prolog was used by David Hovell the Windows NT network configuration to enforce consistency [1][2].
I've heard it's been removed since, so doesn't count as "modern". It's a _beautiful_ application of Prolog though.
[1] https://news.ycombinator.com/item?id=36821871 which leads to https://web.archive.org/web/20030218034509/http://www.resear... -- very worth reading! [2] https://news.ycombinator.com/item?id=14046420
> Racklog is an embedding of Prolog-style logic programming in Racket. “Embedding” means you don’t lose Racket: You can use Prolog-style and conventional Racket code fragments alongside each other. Racklog contains the full repertoire of Prolog features, including meta-logical and second-order (“set”) predicates, leaving out only those features that could more easily and more efficiently be done with Racket subexpressions.
https://docs.racket-lang.org/racklog/index.html
Racket also has GUI, and basically everything else you may need.
How about good docs and examples, community, decent perf for small apps at least?
Racket newbie here. Had just played around with a little some years back when it was called MZ Scheme, PLT Scheme or such. There was an IDE called Dr Scheme, IIRC. I guess all that is part of Racket now.
The performance is around Python / general scripting level; perfectly acceptable.
As you guessed, DrScheme became DrRacket.
You can find more info at https://www.racket-lang.org, presented much better than I could do here.
Performance: closer to Groovy, Clojure or Raku than to Python, Ruby or JS. That is, startup is not as fast, but execution after that is decent. For a few years, performance improvements were not the focus of development due to the migration from custom VM to Chez Scheme. Now Racket on Chez is the default and I think we'll see further improvements on that front (easier maintenance and better potential performance were the reasons behind the migration IIRC.)
The community is small, and the core strength of Racket is not something normal programmers use in day to day programming (that strength being the ability to shape the syntax and semantics of your code at will, ad hoc, anytime, with no friction at all, in any way you want). On the other hand, the results of giving that power to a bunch of very smart people are really worth seeing.
I say the same thing about CUE these days, it can be a real mind bender but gives us a new way to think about taming config and schema complexity. CUE is also much easier to add into existing workflows to turn that logical programming glory into pragmatism.
https://cuelang.org | https://cuetorials.com
The language is going to a foundation (still waiting for update from the CUE team), fun fact, it started as a fork of Go (for the lexer, parser, and a bunch of goodies in that source tree)
CUE's history is from borg, borgfig, GCL, but takes the approach Marcel always wanted to if not tied to the existing practices in a large company, namely Typed Feature Structures and logical programming (no overrides, layering, inheritance, general recursion)
We're about to release a TUI for working with CUE, and hoping that it will make learning much easier for people. We do a lot with Cuetorials, but there is only so much you can do on a website (/rant WASM limitations). Using the Go SDK is the way to unlock CUE super powers.
https://github.com/hofstadter-io/hof
But I don't know if they still support prolog (or maybe as a plugin provided by them for the migration)
Prince's [3] base is Mercury and integrates Rust and C without fuss.
[1] https://mercurylang.org/ [2] https://mercury-in.space/crash.html [3] https://www.princexml.com/
https://wordsandbuttons.online/SYTYKC.pdf
https://terminusdb.com/ is almost entirely written in prolog. + a little bit of Rust and JS.
If you're interested in doing Prolog with Python, have a look at projects like: https://github.com/photonlines/Python-Prolog-Interpreter
I asked him how he actually used it and he basically said what you wrote here: He implemented his own interpreter (in C#) and limited backtracking considerably (as far as I understood) in order to make it useful performance-wise for their use case.
This paper is also very interesting. It describes the Janus System, a deep integration between Python and Prolog: https://arxiv.org/pdf/2308.15893v1.pdf
Anyone currently in education care to comment?
If you were a teacher now, having to explain about predicate logic and proof automation, would you choose teaching Prolog? If not, which language would you choose?
If I knew alternatives that were very recent, like 4-5 years old, I'd be careful replacing the current courses, because often in technology, statistically, if something has been there for a long time, it'll be there for a long time, and many new things die young.
Many but not all do also have logic programming in some form. Through that might be just some side project of another course, or might not be done using Prolog.
I tried to generate prolog from natural language using GPT4 with sometimes interesting results.
https://github.com/HumanAssistedIntelligence/logical
It was frustrating and annoying at first, but once you get comfortable, surprisingly fun (and also a great intro to recursion, as that's the only way to iterate).
At university we were largely taught Prolog by mathematicians who exclusively worked in such languages. Prolog was a very natural way of thinking for them, and many of them had never really used other non-logic/functional languages. For most of the students, who had worked mostly with procedural languages before, it was confusing and frustrating, we would all get tied up trying to use Prolog as if it were some other language.
It wasn't until my third or fourth course that used Prolog, and having studied the algorithm behind it, that I really got my head around how to use it well.
Later on I was helping an undergrad I knew was having problems with Prolog. It was mostly about how to change the way he thought about solving problems to fit in the Prolog world, rather than trying to make Prolog behave like all the other languages he'd used (he was a great developer in everything except Prolog). After quite a bit of discussion there was a real epiphany and he suddenly got it and he proceeded to nail the coursework.
FTFY :P
I find Prolog very challenging for several reasons. I identified 3 levels of difficulty with it:
1) Syntax
2) Reasoning and Paradigm
3) Implementation
1) is things like "\=" for not equal. Those are mostly just inconveniences and Prolog's age is showing there. But I still don't have a firm grip on the pattern matching of lists.
2) is the whole stuff around switching mentally from functions to predicates. I often find myself trapped in trying to reason functionally and if I was very proficient in functional programming that might even work. But so far I'm still at the level of trying to wrap my head around the fact that there no return values and that sequencing via comma (which is actually a conjunction - AND) will be evaluated "kind of" procedural by serial evaluation. This makes it seem familiar but conflicts with the actual paradigm.
3) is the most critiqued aspect of it. There are many Prolog implementations it but they all seem to suffer from the same problems. Like Prolog getting lost in some tree which forces the developer to understand what the interpreter is doing to help it find the right branch. Also the aforementioned confusion that logically (A AND B) is identical to (B AND A) but in Prolog isn't and can make the subtle difference between the program finishing successfully or running until memory is consumed.
In my opinion there is reason why Prolog is one of those subjects that are brought up here again and again and again but without any justification with regard to something about it being "news". It's a very fascinating paradigm and I find it very sad that it doesn't really get any attention today. Just check the list of literature about it - there's almost nothing and what there is either old or distinctively academic.
One reason clearly is the lack of convenient interfacing to other languages. Because even a Prolog genius would probably admit that there is no point in trying to implement anything IO with it.
I'm working through Learn Prolog Now. Which is a good book. Already ordered Prolog in Adventures. I could see several projects for it that would be a natural fit but I already anticipate that eventually my enthusiasm will fade at the point where I'm forced to dig into SWI as soon as I leave the toy level. SWI is also kind of weird. It seems to be the best choice but there are surprisingly trivial aspects about it that I find very unsatisfying (I can load a DB with "[filename]." but I cannot purge it before loading another DB without restarting the session ... or does somebody know how that's done?). That's basically what I observe as far as my as well as other people's experience goes with it: very promising and exciting but eventually disappointing. It shouldn't be like that.
I also believe that symbolic AI / Prolog and deep learning could very well benefit from each other.
1. Prolog is often used in things that are kept very secret. There is a relatively small set of professional and highly committed users centered on distributions like SICStus. There is a much larger set of hobbyist/academic users centered on distributions like SWI, Tau, Ciao! and so on. The feature sets of the commercial distributions gives you some idea about the sort of capabilities the hardcore users require.
2. Getting good at solving problems with Prolog is a borderline mystical experience -- or at least it is for me. Seeing the logical structure of problems, their representation through symbolic pattern matching and their solutions as search, is an insight which once seen cannot be unseen. It will condition your perception of problems forever more.
As in national security secrecy?
Pyrosequencing AB: SICS developed a dispensation order generation algorithm for Pyrosequencing's sequence analysis instruments, using constraint programming with SICStus. The algorithm can be described as a compiler, which calculates an instruction sequence based on an input specification. Applications include genetics, drug discovery, microbiology, SNP and mutation analysis, forensic identification using mtDNA, pharmacogenomics, and bacterial and viral typing.
That's a public example from the SICStus website, but you could see why the example above could have remained a secret and their Prolog used along with it. SICStus attracts such cases.
I coded in Prolog for many years and I was reasonably proficient in it, but I only really understood it during my PhD when I finally had the time to study logic programming textbooks and understand where it comes from.
Unfortunately, that is simply not the amount of time that one has unless one is doing a PhD -and one in a relevant subject, as I did.
As a for instance, \=/2 is not "not equal", but "does not unify". So for example A \= B is false, because A and B are variables and unify, a \= b is true because a does not unify with b and a \= B is false again because a unifies with B (as in a = B... which is not equality, or assignment, but unification). I'm guessing that your trouble with pattern matching (i.e. unification) of lists comes from the same place. All this will inevitably and understandably sound downright weird unless you know why unification exists. But that means you need to understand Resolution, and logic programming.
Regarding (2), indeed, Prolog predicates are relations, not functions. It might help to think of the er relation between a function, and a relation. Say for example the addition function: x + y = z; or, in Polish ish notation: +(x,y) = z. If you wrote that as a relation: +(x,y,z).
Meaning you _could_ see a relation as a function with just one extra argument that holds the return value of the function. If you have trouble transposing the function-like representation of code in your head to a relational one, for Prolog, try doing it that way. Instead of trying to write functions that are replaced by their return values, write functions+return that have one extra variable for the return.
So, sort(List) = Sorted becomes sort(List,Sorted). reverse(List) = Reverse, becomes reverse(List,Reversed) and so on. Ingore for a moment the usual comment about how Prolog predicates do not have "return" variables, only terms in some relation. True, but if it's blocking you, avoid thinking about it. Until you can think about it.
Btw, the reason that Prolog predicates aren't replaced by their value, not even their truth value, is that Prolog inherits its semantics from First Order Logic (FOL), where there's also no replacement of values. In fact, FOL _has_ functions, but they, too, are not the kind that is replaced by their value. I think that kind of function is only found in programming languages and then not all of them either.
The reason for (3) also has to do with Resolution and how it is most efficiently implemented. There are alternatives, e.g. datalog, or Answer Set Programming, which are more purely logical; or Prolog with tabling, a.k.a. SLG-Resolution, which doesn't suffer from left-recursion (what you get when you put a recursive literal first in the body of a clause).
>> Because even a Prolog genius would probably admit that there is no point in trying to implement anything IO with it.
I'm no Prolog genious, so what does "anything IO" mean?
This helped me to appreciate Prolog as an ongoing, progressive movement in programming.
"You may not believe this, ..."
or anything similar in technical documentation, blogs and similar
making assumptions about what other people think (or say) is belittling/impolite
it's also stylistically has a bit of a "sensationalist" touch, which just makes it feel misplaced in this kind of articles IMHO
https://www.eng-tips.com/viewthread.cfm?qid=90305
but practically there is a difference between thinks people believe are objectively true and things which they are less sure about or find quite subjective
IMHO and AFIK are like an emoji they add additional subtle context and should in no way be read literally
like emoji they do not belong in technical articles, but are normal in more short form text conversations
Science is full of authors supposing what other people believe and then surprising them with their findings.
"Remarkably", "perhaps unintuitively", "convention states", "a striking consequence", "a surprising result", …
You may not believe this, but here's Feynman making assumptions about what other people think:
Because atomic behavior is so unlike ordinary experience, it is very difficult to get used to, and it appears peculiar and mysterious to everyone—both to the novice and to the experienced physicist. Even the experts do not understand it the way they would like to, and it is perfectly reasonable that they should not, because all of direct, human experience and of human intuition applies to large objects.
Here's Newton using "which is an erroneous Supposition", his version of "you may not believe this":
Are not all Hypotheses erroneous, in which Light is supposed to consist in Pression or Motion, propagated through a fluid Medium? For in all these Hypotheses the Phaenomena of Light have been hitherto explain'd by supposing that they arise from new Modifications of the Rays; which is an erroneous Supposition.
As Albert Einstein wrote:
The whole of science is nothing more than a refinement of everyday thinking. It is for this reason that the critical thinking of the physicist cannot possibly be restricted to the examination of the concepts of his own specific field. He cannot proceed without considering critically a much more difficult problem, the problem of analyzing the nature of everyday thinking.
How can you do that without making assumptions about what other people think?
are very different to "You may not believe this,"
because they don't say "you" they state it in a general way which is ambiguous about weather it was e.g. "remarkable" for them or many they know
so they don't make assumption about every specific reader, instead they make assumptions about themself and/or readers in general
The examples you state all are different and in my opinion miss the point. In no case do they say "you ..." or anything close to this. It's always in a genericway. In other words "even experts will find it peculiar", "theories of this kind are erroneous", "he - as in a generic scientist - needs to consider something".
Anyway, wonderful site and I'm glad to see this topic recurring.
It is also a logical paradigm and it is worth spending the time understanding how [proof by negation] + [unification] = [logical programming]. For me it is without doubt the most mind expanding programming language I've ever learned.
Working with CSV or JSON data and performing relational ad hoc queries or employing Prolog for combinatoric test/coverage data generation is also very convenient.
[1]: https://quantumprolog.sgml.io/bioinformatics-demo/part1.html
https://emiruz.com/post/2023-04-30-prolog-for-data-science/
https://emiruz.com/post/2023-05-12-semi-supervised-clusterin...
If I had to do a lot of work with strings, and I had to choose between, say, C and Prolog, I'd definitely go with Prolog. If it was Python vs Prolog, it would depend on a task...
Prolog has two problems for other kinds of applications. First, it doesn't play well with libraries and other languages, because the paradigms are so different. Second, doing any sort of decent GUI is close to impossible, again, because it doesn't fit the paradigm.
You could say these aren't "interesting" interactions. But this is how it would work because libraries don't really exploit the useful features of Prolog. You could, in principle, write "thicker" layers of interop, where you'd probably have to aggregate multiple library functions into single predicate interface on the Prolog side, where a specific sub-routine would be called based on instantiation mode of the arguments. But this is hardly different from exposing C libraries to object-oriented languages (like Python), where you aggregate multiple C functions to act like property accessors or to act like methods of a data-structure etc.
Biggest downside "back in the day" was interop with other things was tricky. Either you embedded a Prolog in a JVM or something like that (usually with less features) or you relied on some sort of C binding. My takeaway was it's great so solve certain tasks (especially CSP) but not for solving tasks with a user interface in the mix (or file system access or any miriad of other things).
"Back then" SWI was also lagging behind and we used some proprietary solution which I'm generally not a fan of when it comes to programming languages/environement (Sicstus iirc.). Last time I checked SWI has caught up with the prorietary stuff and would be my go to now though.
The Prolog exam was also the one I was most afraid of during university because it consisted of a 60 Minute live coding section (solve three tasks of increasing difficulty) followed by an oral exam. My prep experience was that often times it just takes some reasoning and sitting there before the magic moment comes and the Prolog solution pops up in my brain...not exactly what you want in a timed exam situation. Thankfully I aced it by spending 40 minutes on the easiest task and solving the other two in a matter of minutes. The follow up course was a lot of fun (implement a game playing agent and compete against other teams in a tournament).
"The Art of Prolog" and "The Craft of Prolog" were also two of the better programming books I have read in my life.
Still remember the expressions of the undergrads walking out of our first Prolog lecture. Equal parts shell-shocked and worried.
There's a thought: I wonder how the chatbots handle Prolog. Have they seen enough of it? Can they "reason" in that way?
The methods for writing imperative programs in Prolog are really clever but they are also awkward. (For instance "success" is often represented as a logical false and "failure" as a logical true)
I think there has been an explosion of interest in a pure logical dialect of Prolog called Datalog
https://en.wikipedia.org/wiki/Datalog
when I first got interested in the semantic web I thought "OWL sucks" and thought Datalog was an interesting alternative, although it was invented in 1986 there was very little literature on it. I got dragged kicking and screaming away from the semantic web multiple times and when I got back to it again recently I found that the opinion "OWL sucks, use Datalog instead" has gotten widespread and now there are a lot of Datalog implementations and a huge literature. (Oddly, I met somebody who taught me the true meaning of OWL and how to do interesting things with it.)
If you think that materialized views, CTEs and such are a ugly and disorganized mess in SQL you might like datalog a lot. (Of course if you think triggers are a disorganized mess you might like production rules but I haven't quite seen a production rules-based database that really works.)
I'm confused by this. false/0 (or fail/0, equivalently) is used to force a clause to fail. Frex:
Will always fail, not succeed. So what do you mean?Also, "false/0" is extra logical.
IMHO, The Art of Prolog is along with Concepts, Techniques, and Models of Computer Programming, Paradigms of AI Programming, and Structure and Interpretation of Computer Programs the canon of dynamically typed programming. The Craft of Prolog is also exceptional but has some chapters that are too specific to programming in old Prolog systems and thus outdated.
Lots of Prolog ideas live in answer set programming and Datalog. The latter is fantastic to build state-of-the-art static analyzers [1].
[1] https://arxiv.org/abs/2012.10086
I might be buying the book if I really like it to support the authors, but it’s great that they made it available for everyone.
I got my copy back in the (gasp) '80s from the neighborhood B. Dalton's or something, and it totally changed the way I thought about the roles of code and data. It's one of the handful of books that have survived all my library downsizing, along with The Tao of Programming, the bible, the dragon book...
https://shenlanguage.org/SD/Prolog.html
And the chapter from The Book of Shen:
https://shenlanguage.org/TBoS/tbos_359.html
Tarver has written two books with accompanying software on logic programming: Logic, Proof and Computation:
https://shenlanguage.org/lpc.html
Programming the Logic Lab:
https://shenlanguage.org/logiclab.html
[1] https://www.youtube.com/watch?v=lMcRBdSdO_U
[2] https://www.youtube.com/watch?v=BUJNyHAeAc8
(Seriously though, there are many things that could be considered successors, like Mercury or maybe even Datalog, but to a first approximation I'd say that Prolog is still timely.)
Scryer Prolog can now run on WASM and in browser, client side.
Prolog seemed a good fit, as I wanted to find all possible parses of words, and the backtracking mechanism solved that. I also need to handle morphosyntactic features (like person and number), and Prolog's unification algorithm provided exactly what was needed.
We had to implement a Sudoku solver and at some point I got stuck. I was trying to bring over my imperative programming mindset, I had written a solver in Java before and I tried to port over the Java code to Prolog, which did not work at all.
So at some point I started over, and wrote some simple Prolog code to check if the Sudoku is valid. Then I realized that I could also run these predicates to solve the Sudoku, and I completed the solver in under 10 lines.
It was a real eureka moment, Prolog is very powerful, but criminally underused in practice, fortunately I had the opportunity to work with Datalog and OWL.