Tar.pl – A tar creator and extractor in ~130 lines of Prolog
github.com
github.com
I recall reading about this somewhere, but haven't seen such nice real world example before. Well done.
https://github.com/mndrix/bencode/blob/master/prolog/bencode...
It is also cool that is completely transparent whether a predicate returns a single value or multiple values. It's sort of like if every function were implicitly a generator.
(Also here's a gratuitous plug for my employer's product in a similar vein: https://docs.relational.ai/rel/intro/overview)
yes; yes; yes; yes; yes; yes; yes; yes; ...
Datalog, on the other hand, isn't. It's terminating, like SQL. And many program properties are indeed decidable.
Because of it's restrictions it traditionally uses different search/resolution strategies than Prolog. And in the other direction the SLD resolution strategy Prolog traditionally uses can fail on syntactically valid Datalog (clause ordering issues).
That said, the big Prolog implementations all support multiple resolution strategies although I suspect there are a lot of various optimizations Datalog squeezes out of it's restrictions that let it handle things that might not be reasonable in a given Prolog.
Can you elaborate on what you mean by this? Like, custom database implementations? I wrote a little bit of Prolog in college and thought it was neat but I haven't looked at it since. I've seen Datalog mentioned recently in relation to biscuits, a technology I think is incredibly cool but that I don't fully grok.
.pro is also common for Prolog code, if you care about the extension clash.
Why not just `.prolog`? In this day and age. (Maybe there’s some reason like “then I can’t put these source files on FAT filesystems”, I don’t know.)
Java seems to do fine with it’s longer-than-three-letters `.java`.
The documentation for SWI-Prolog 5.10 (2010) even says "Tradition calls for .pl, but conflicts with Perl force the use of another extension on systems where extensions have global meaning, such as MS-Windows. On such systems .pro is the common alternative." https://www.swi-prolog.org/download/stable/doc/SWI-Prolog-5.....
Thing is, I can't confirm that tradition predates Perl 4 - call it 1993.
Turbo Prolog used ".pro" in the late 1980s - https://archive.org/details/bitsavers_borlandturOwnersHandbo... .
So did Prolog-2, according to the 1990 book at https://archive.org/details/advancedlogicpro0000unse/page/80... .
VPI Prolog from 1991 has an '".hc" file name extension (which stands for Horn Clause, the logic upon which the PROLOG language is based).'.
I even found one Prolog system from that era using ".txt", at https://archive.org/details/programminginpro0000cloc/page/26... .
Maybe the 1975 Prolog I manual has more to say...
I think it's even more explicit on p25 where it says "A module name can have the form either file.ext or just file. In the latter case the extension 'PL' is implied."
No idea if Emacs ever fixed it (this was 2009 or so).
It's basically the flavour of Prolog that won the popularity contest exactly because of its academic sponsorship (by the University of Amsterdam).
In fairness I was a shocking student and deserved it.
Which makes sense; my understanding is that in Prolog, insofar as a function encodingOf(A, B) is defined by using A and B on either side of a bind expression to equate B as the encoding of A, if encodingOf(A, knownEnc) would act as a parser for knownEnc, then encodingOf(knownDec, B) would act as an encoder for knownDec — essentially swapping the question "what would this parse to" given an encoded binary, for "what would parse to this" given a decoded term.
Erlang can't do exactly that — a given variable's "bound-ness" is determined during compilation, and head-clause variables can't be indeterminately bound — but if it could, then you would indeed get an encoder for the same format "for free", since binary-pattern matching in Erlang becomes a binary-building expression depending on whether the variables in the expression are bound or unbound.
(Now I'm curious how hard it would be to add indeterminately-bound compile-time function polymorphism to Erlang — i.e. generating bytecode for the powerset of bound-ness-es of the variables in the head clause, only where at least one generates valid bytecode, probably name-mangled with a bitfield suffix of said bound-ness-es; and then being able to bind function parameters from such functions as new output variables in expressions, where the call sites also generate a call to the equivalent mangled name. The usefulness of this would probably be pretty small, without the other aspects of Prolog, but you never know...)
some_predicate(<concrete values>, Decoded). % decoder
some_predicate(Encoded, <concrete values>). % encoder
some_predicate(<concrete values>, <concrete values>). % validator
But in Prolog, that's what many predicates permit as a baseline. In Erlang, yes, a validator is, or can be, a parser/decoder. But it's not an encoder which still needs to be a separate function (or set of functions) to describe movement in the other direction.https://learn.microsoft.com/en-us/dotnet/csharp/language-ref...
Also not sure what does it have to do with defined/undefined behavior. Even if it is not verifiable, the behavior is still defined.
Around 30 years ago I was really into Prolog but recently I have tried getting back into it and I am having a rough go at it. I still have the old Art of Prolog book, so that might be a good reentry point.
Fwiw, doing problem setup and result processing in another language can avoid much pain. So prolog only where it excels, and a mainstream ecosystem for the rest. I've liked batch mode "write a prolog file, run it, read result (eg json, or wrapper-language code to eval)", but was recently thinking of trying PySwip[1].
I should give Scryer Prolog a go to see if it is any better, and I'm by no means an expert Prolog programmer so YMMV.
Seems like a common chicken-egg problem of niche languages. Performance doesn't improve until lots of people are using it, but lots of people won't use it until performance improves.
#!/bin/bash
tar "$@"
Takes the same arguments as the original! tar(dstTarFile, srcFiles)
Next you provide one of the two arguments and query Prolog to find the other: you pass in a tar file as the first argument, and it tells you what `srcFiles` would need to be to create that archive, effectively extracting it. Alternately, you pass in `srcFiles` and it tells you what the corresponding dstTarFile should be, creating an archive.Another way to think about it is that the Prolog code is not directly telling you how to read/write tar files. Instead, imagine it as a cost function that tells you how well this tar file corresponds to the uncompressed representation of those files, which you're minimizing.
If you were to write it in C, the code would imperative: do this, then that, then this other thing. Consequently, the reader and writer code are totally different. For example, to process the file size, you would write functions two functions:
char* uIntToOctalASCII(unsigned int sz)`
and octalASCIIToUInt(char* szstr).
Semantically, these are inverses[0]: octalASCIIToUInt(uIntToOctalASCII(x)) == x
but the implementations have very little in common. At best, you can use one to test the other.[0] And leaks memory, but whatever....
open('test.tar',read,TarFile)
HSizes = [100,8,8,8,12,12,8,1,100,6,2,32,32,8,8,155,12],
maplist(length,HBytes,HSizes),
append(HBytes,~read_bytes(TarFile,512)),
HBytes is now a list of lists of your bytes, do with them as you please, we can even name them if we want HFields = [ filename, filemode, ownerid, groupid, filesize
, modificationtime, checksum, typeflag, linkedfilename
, ownerusername, ownergroupname, devicemajornumber
, deviceminornumber, filenameprefix, padding],
keys_and_values(HFields,HBytes,Header),
you could push it off into a predicate and it'd even more or less work relationally and it'd probably be faster than the DCG...but it wouldn't be interesting.
And like they said https://news.ycombinator.com/item?id=34431450
It's hard enough to write things in PROLOG that _are_ ideally suited for the language - like natural language parsers (they used to be LISP or PROLOG, but these days they are all in C++ or FORTRAN wrapped in Python so no-one gets hurt; why? Because of efficiency and numeric libraries required - modern parsers for human languages implement probabilistic or neural models, no longer symbol manipulation and pattern matching).
EDIT: I'm not a PROLOG hater - in fact, enjoyed reading the article (and I will read sequels on implementing "mount" in RPG3, "htop" in COMAL etc.) and my friends include some PROLOG lovers. I also find it important and stimulating to learn about another paradigm, and logical programming is an important one for many reasons. This rant is more about using it on the specific task of implementing "tar", for which it is a poor fit, as the article's PS shows.
Moreover, Prolog in particular requires a complete mindset change in order to naturally write in it. Once you reach that level - anything is possible and even not that hard to write in Prolog.