3 ms·
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 program
by YeGoblynQueenne 3y ago
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.
- bw86 3y agoDo you have any hints how to use Prolog in a way to avoid making a mess? Which Prolog implementation(s) did/do you use?
- YeGoblynQueenne 3y agoDo I have any hints? Oh dear. The advice in this technical report is sound: Coding Guidelines for Prolog https://arxiv.org/abs/0911.2899 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: 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. And then I can do: ?- initialised_state_vector(_Vs1), modify(position,increment,x,_Vs1,_Vs2), inspect(position,P,_Vs2). P = position(x(0.1), y(0.0), theta(0.0)) ; false. 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: inspect(group,GroupId,group(GroupId,Contacts,Count),[group(GroupId,Contacts,Count)|_]). inspect(group,GroupId,group(GroupId,Contacts,Count),[_,group(GroupId,Contacts,Count)|_]). inspect(group,GroupId,group(GroupId,Contacts,Count),[_,_,group(GroupId,Contacts,Count)|_]). inspect(group,GroupId,group(GroupId,Contacts,Count),[_,_,_,group(GroupId,Contacts,Count)|_]). inspect(group,GroupId,group(GroupId,Contacts,Count),[_,_,_,_,group(GroupId,Contacts,Count)|_]). So that I can access the elements of a list-argument at constant time. So that's effectively compiling a Prolog list to act as an array, or a dictionary I guess. I don't know how big the "compiled" lists have to be, so I set a configurable "budget" before I generate the access predicates above and hope for the best. An alternative would be to simply add more inspect clauses for list-arguments as needed by using asserts but that's only going to happen over my cold, dead body. All this could have been done much more simply if I used assert/retract, and also used Prolog lists as they're intended to be used, as flexible linear-time access structures. But if I did that, I'd end up in a world of pain later on. I know that from experience and I have the scars to show for it. I suppose my approach could be seen as jumping through hoops to do something that is easy in other languages - just write some stuff to a database. But, I've worked with OOP languages on backends accessing relational databases all the time I was in the industry, and it's anything but easy. It's a constant source of pain instead: somehow every OOP textbook I've ever read forgets to tell you this, but OOP languages are simply not good at handling data. In Prolog at least my backend and my database are one and the same, my code _is_ my database, and I can has a clean and tidy representation where my entire program's state is in the source code (rather than spread out between the source and the database, and between the source and the runtime). So it takes a bit of work. And what doesn't? My scheme above also serves to preserve the "logical purity" of Prolog, since accessing the state-vector is side effects-free, but that was really not my motivation. I don't care about purity. However, in Prolog, when you leave the declarative, logical, immutable paradigm behind and enter the world of destructive data structure updates, you enter a world of pain. The dynamic database is evil. OK, so this got a bit too long, by far. You asked for a "hint" and I had to go and write a little white paper as usual. But I hope all that is useful to you, as a kind of case study. Edit: I use SWI-Prolog. It's free and it's got the most libraries. Also a helpful community and a main developer who is clearly superhuman.
- bw86 3y agoThank you - this was insightful. 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.
- YeGoblynQueenne 3y agoI'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.