Emacs Babel for Prolog is "goal-oriented"

I recently resumed my quest to get literate programming going in Prolog. I’m on latest Debian/Emacs/org-mode/SWI. I do have jupyter notebook going, but Emacs org-mode is simply a better, more capable, flexible way. So I finally got Prolog code blocks to sort of work. I had to ditch the 1.29 ancient prolog-mode for the even older 1.22. But now I can get some functionality:

#+name: 21903f70-70e6-4274-b379-362f505a990d
#+HEADER: :session *prolog-1*
#+begin_src prolog :exports both :results verbatim
likes(george, swimming).
likes(susie, tennis).
likes(susie, swimming).
likes(mary, X) :- likes(susie, X), likes(george, X).
#+end_src

This works, responding with true. But then

#+begin_src prolog :exports both :results verbatim
likes(mary,X).
#+end_src

simply complains about a re-defining and gives nothing. Apparently, reading through the actual ob-prolog.el code, everything has to be constructed as a goal. But then, as you can see, this does work

#+name: 0c1f6bcc-0664-41bb-ba55-3be491bec55e
#+HEADER: :session *prolog-1*
#+HEADER: :goal ml(X)
#+begin_src prolog :exports both :results verbatim
ml(X) :- likes(mary,X).
#+end_src

#+RESULTS: 0c1f6bcc-0664-41bb-ba55-3be491bec55e
: X = swimming.

So in the code block header I must state a specific goal/consequent and have any query set up as a head/body… Okay, if you insist. But then something like this seemingly can’t be done

#+name: d86cee0b-f33f-4804-9b6f-6393d0b0de2b
#+HEADER: :session *prolog-1*
#+HEADER: :goal
#+begin_src prolog :exports both :results verbatim
=(mia,mia).
#+end_src

#+RESULTS: d86cee0b-f33f-4804-9b6f-6393d0b0de2b
: 
: ERROR: user://29:362:
: ERROR:    No permission to modify static procedure `(=)/2'

Likewise

#+name: 132a4294-16c9-4132-97aa-5fa43c3c8bc2
#+HEADER: :session *prolog-1*
#+HEADER: :goal eqls(A,B)
#+begin_src prolog :exports both :results verbatim
eqls(A,B) := kill(shoot(gun),Y) = kill(X,stab(knife)).
#+end_src

#+RESULTS: 132a4294-16c9-4132-97aa-5fa43c3c8bc2
: ERROR: Unknown procedure: eqls/2 (DWIM could not correct goal)

i.e., trying to turn the “=” into a goal-oriented query has me lost in endless guess-and-test. This failed

+name: 612cd1ea-e5cc-47d4-a6cf-1a4487e0134e
#+HEADER: :session *prolog-1*
#+HEADER: :goal miaeq(A)
#+begin_src prolog :exports both :results verbatim
miaeq(A) :- A is =(mia,mia).
#+end_src

#+RESULTS: 612cd1ea-e5cc-47d4-a6cf-1a4487e0134e
ERROR: Arithmetic: `mia/0' is not a function
ERROR: In:
ERROR:   [14] _5974 is (mia=mia)
ERROR:   [12] '<meta-call>'(user:user: ...) <foreign>

As a first-chapter beginner starting over again, is it realistic, viable to have everything I want to query be in the form of a head/body, i.e., the head is the goal? How would I turn the query =(mia,mia). into goal format to satisfy Prolog Babel?

I definitely like using Emacs org for literate programming! I’ve done it with Prolog before too; I think the key thing is when you want to do a query instead of define things, you’d write it with ?- query.; e.g. ?- likes(mary, X), ?- =(mia, mia). etc.

I also find using :noweb is helpful for piecing things together from multiple definitions. For example, this blog post has its source an org document, with the code samples run in babel, which looks like this.

Hmm. Still having trouble

#+name: 21903f70-70e6-4274-b379-362f505a990d
#+HEADER: :session *prolog-1*
#+begin_src prolog :exports both :results output
likes(george, swimming).
likes(susie, tennis).
likes(susie, swimming).
likes(mary, X) :- likes(susie, X), likes(george, X).
#+end_src

#+RESULTS: 21903f70-70e6-4274-b379-362f505a990d
: |: |: |: |:
: true.

then

#+name: c7adfeff-b292-4543-a5cf-4a58808b9eba
#+HEADER: :session *prolog-1*
#+begin_src prolog :results output :exports both
?- likes(mary, X).
#+end_src

#+RESULTS: c7adfeff-b292-4543-a5cf-4a58808b9eba
: Warning: user://2:17:
: Warning:    Singleton variables: [X]
: |:
: true.

Not really X = swimming. Can you see what I’m doing wrong? But still

#+name: 0c1f6bcc-0664-41bb-ba55-3be491bec55e
#+HEADER: :session *prolog-1*
#+HEADER: :goal ml(X)
#+begin_src prolog :exports both :results verbatim
ml(X) :- likes(mary,X).
#+end_src

#+RESULTS: 0c1f6bcc-0664-41bb-ba55-3be491bec55e
: X = swimming.

This was also posted at Reddit

I would like to see an explanation as to why GitHub - ljos/ob-prolog: Org babel functions for prolog evaluation. is involved - just seems to be causing confusion, what’s the benefit?

Start with normal Prolog, and learn the basics of Prolog.

jamesnvc above actually reminded me of an alternative way to use org-mode Babel, and that is to not evaluate the code blocks in situ/directly, rather, have them tangle out into a regular .pl file, then consult the .pl file at your leisure. Keep the literate philosophy, forget the Babel code block evaluation headaches.

1 Like

I believe it’s because the queries in babel aren’t treated like a top-level; since the X isn’t being used, it isn’t displayed by default. I generally work around this by explicitly doing something like ?- likes(mary, X), format("Mary likes ~q~n", [X]).. I’m not sure if there’s some easier way to make ob-prolog act like a toplevel.

“Literate programming” as practiced by its inventor can be seen for example in the metafont implementation (link to a PDF). You can find for example a solver for linear and non-linear equations somewhere in parts 28 and 29, or a bunch of numerical routines for calculating coordinates on a plane using small integers only, parts 7 and 8.

To the best of my knowledge, R Sweave was the first implementation of a “dynamic report generation” that uses some of the tools of literate programming in the context of statistical data analysis. (Maybe the literate programming support in Emacs org-mode predates it? Do you @jamesnvc know?)

However, R Sweave and all tools in that pedigree (knitr, Jupyter) are less useful than weave and tangle. They all support one way of presenting code and results of the computation but you need to keep strictly in the “dynamic report generation” paradigm to make it useful.

As I mentioned above, Emacs is plagued by abandonware, and I fear this is the case here. How can we get a basically working strategy together with as-is Prolog Babel? Even if it’s kludgy, that’s better than nothing. If you look at the ob-prolog github site there’s zero docs, just a blurb or two inside the actual code as elisp comments. FYI, I couldn’t get the jupyterlab prolog kernel to answer queries either… So Prolog is strictly create code file → consult → query at the command line REPL? Does anyone try to do literate?

See my post above for a bit of context. I first wrote it then thought it’s irrelevant but now its back… The main message is:

  • If you need to run a data analysis and you want to make it reproducible: then sure, use this specific instance of “literate programming”, ie Sweave, knitr, Jupyter etc.
  • For generic literate programming, you must tangle and weave your source.

There is the noweb tool, it is ancient but it is still available in most Linux distros and it does about half the job. The original WEB works only for Pascal, CWEB only for C and so on. Noweb’s tangle works for anything. Sadly, the weave makes some very specific assumptions, for example, that you want to use TeX :smiley: . I once re-implemented weaving based on Markdown, using some Prolog code and Pandoc, but this code was also strictly meant for the “literate notebook” way of working and not for generic literate programming as intended by Donald Knuth.

Is literate programming outside of the “reproducible data analysis” paradigm is a good idea? I no longer say either yes or no. I guess it depends. The PDF I linked above (Metafont) demonstrates that it is the best idea ever; but somehow it didn’t catch on. Metafont also didn’t catch on even though it is also a brilliant tool for solving a concrete need.

I think the first problem of literate programming is it adds complexity to the program development process. Instead of write in any text editor following the language syntax, save compile run, you now have to learn a new syntax, and have extra tools to do weave and tangle (I’m not exactly clear what these are), and in any event, when it comes to sharing code, instead of sharing .py files everyone is equipped to use, you now have to share .litpy or whatever, which most people don’t have the tools for. So it’s a case of the entire system being a bottleneck for sharing.

So now, Jupyter has had some success at least for educational purposes because it allows you to create this nice interactive document. I’m not clear what the difference is that distinguishes generic literate programming from “literate programming”, and I would like to understand.

I think there is a future for literate programming but it needs to be reinvented to smooth the consumption process by everyone who is not into literate style.

I consider the use of structured comments (e.g., /** ... */ or %! ....) as used by many documentation systems a good compromise. In particular when combined with Markdown. As is, it is more about documenting source code, but I guess there is no real reason why it couldn’t be extended to be more like a notebook.

In the Prolog world we also see a directive based approach to documenting (Logtalk, Ciao, ECLiPSe, …?) I’ve always preferred the structured comment approach. A file with structured comments can be loaded into any Prolog system. When directives are used you’ll need some infrastructure to define the operators and directives (implement as dummy predicates for systems that call any :- ... or use term_expansion/2 to map them to []).

SWISH already supports notebook-style literate programming in Prolog, right?

The notebook, the interactive document you talk about, is an extension of the REPL model of interaction with the computer. To take Sweave, which was implemented for R.

You use R by starting an interactive session, loading data, and doing statistical analysis and plotting, all by issuing commands on the prompt. It is (unlike the Prolog top-level!) stateful. Plotting is to a graphical window separate from the terminal. What Sweave does is that it lets you write a static document, in free text, describing your data analysis. You can embed the commands that you’d run on the interactive prompt in code blocks. You then give this static text document to Sweave and you get eventually a typeset document with your text, the commands you gave to R, and the results and plots you got. The User Manual has a full example within the first few pages.

The interactivity comes from automating this process to run it on all relevant document changes.

While you might create artifacts as you run the code in a notebook (by writing to a file, for example), usually only the human-readable output is included in the report (so, small data structures and plots).

All other notebook tools follow this concept, with some extensions, to the best of my knowledge.

In literate programming, your original literate source has two outcomes. One is the typeset document which includes all code; the code is traditionally pretty-printed. This is the “weaving”. The other outcome is the code, as source code to be given to a compiler or an interpreter. Usually all the free text from the literate source is stripped. This is the “tangling”. Note that the code is usually rearranged during tangling, as the “code chunks” in your literate source can contain references to other named “code chunks” found elsewhere in the document. This rearranging is usually not necessary for notebooks.

I guess you should be able to imagine how weaving and tangling can be used as building blocks for a notebook?

PS: in the context of literate programming, pretty-printing actually garbles the code so that you cannot easily paste to valid source by hand. For example “X >= Y” might become “X \ge Y”. Or maybe this:

foo(X, Y) :- X =< Y.

would become:

\mathrm{foo}(X, Y) \leftarrow X \le Y

You can see this style in some textbooks.

Yes. A SWISH notebook is a sequence of <div> elements with some attributes that defines the role and some actions (e.g., query cells may automatically execute on page load). The save/load format is pretty simple and can easily be interpreted or created outside SWISH. All the complicated HTML stuff is added to it at runtime. It can do a lot of things, varying from letting the user run some queries in a context to fully automatically create a web page with tables, charts, etc. It only does HTML though. Given a good print style sheet it could do more :slight_smile:

1 Like

4 posts were split to a new topic: Documentation systems (PlDoc, LpDoc, etc.)

As an aside, I created a stateful REPL a while ago for CHR development purposes. It doesn’t capture and reuse variables, but it does accumulate CHR objects. With a bit of effort I could probably make a top level for prolog that behaves like:

?- X = 5.
X=5.
?- double(X, Y).
Y=10.
?- X = 6.
fail.
?-

Not actually top level, but more like break mode. I guess the trouble comes when you want to reuse names, I guess you could have a directive to clear a variable or all variables from the working memory. Specifically, the REPL loses the names so you can reuse them. Losing the actual variable objects and their content is up to the GC.

Yes, I remember reading about this. You write your code like you’re writing a textbook. Using modern syntax, it would be like whenever you want to insert code into your text, you use the triple backquote. And perhaps if you are writing the right kind of code, you might want to use LaTeX for the text of your book, or maybe markdown.

The one thing I never understood about the scheme was how logically to structure your program, so it makes sense as a book. A complicated program has hundreds of routes through it, and if your book structure is not module/class/method, then what order do you use? How do you as a developer understand where to find the functions used by your program when they aren’t in program structure order? And if your answer is “tooling designed for Literate Programming”, then you have just committed to the bottleneck I mentioned before: Most people don’t have that tool and therefore can’t edit your program. They can only read the weave and tangle versions.

Seems like a dead end.

The thing I’ve dreamt about forever is a tool where you could write both code and fancy documentation at the same time. I imagine embedding pictures. But since it’s a source editor, that means links in the file, so somebody without the tool gets to click a hyperlink, but with the tool you could see it right there. The document editing system Obsidian gives intimations of this kind of thing. However, it edits .md markdown files, which lets you embed code blocks, and that’s not at all the same thing as editing a code file and embedding markdown in the comments.

Is this like

?- set_prolog_flag(toplevel_mode, recursive).

If I recall well, that saves toplevel variables in a global variable as well, so $Var doesn’t get a copy as normally in the toplevel, but it gives you the original.

Probably like that, but it’s pretty elementary code.

myrepl :- get_input(A), call(A), myrepl.

But then I put extra stuff to do things I want, handle failures, etc.