Can someone compare and contrast Camlp5 quotation vs quasiquotations?

I am currently converting the code from “Handbook of Practical Logic and Automated Reasoning” by John Harrison (ref) into SWI-Prolog.

The handbook code uses Camlp5 quotations and the closest thing in SWI-Prolog is quasiquotation. Translating the code is going fine but an annoying question keeps popping into my head that I do not know the answer.

How are Camlp5 quotations and quasiquotations alike and different?
Note: While I mention the use SWI-Prolog quasiquotation, the question is asking about quasiquotations in general as might be used in other programming languages that note they have quasiquotations, E.g. Haskell, Racket, …

Related questions:
Did one come before the other?
Did they evolve independently?
Are they basically the same thing but with different names? The functionality of converting a string (Concrete syntax) into an AST seems to be consistent among them and that conversion is done at compile time and not at run time.


FYI

I am a Discourse admin on the SWI-Prolog Discourse site with very practical knowledge of Discourse, so can someone bump my Discourse trust level to at least member, regular preferred, so that I don’t have to be limited by the bot. Thanks.

1 Like

Hi!, welcome to the forum!

I’m not too familiar with the history of quotations and their development, more familiar with quotations and quasi-quotations from Lisp, and it seems like this question is asking about the general difference between quotations and quasiquotations.

In lisp, if you quote an s-expression, i.e prepend it with a quote:

'(f 1 (+ 1 1))

it is then interpreted as data rather than code, as the term:

(list 'f 1 (list '+ 1 1))

Quasiquotations allow you to “escape” the quotation, and thereby embed the outputs of arbitrary lisp expressions within the data. In lisp quasi-quotations are started using a backtick, and escaped using a comma:

`(f 1 ,(+ 1 1))

As such, the above will evaluate to:

(list 'f 1 2)

As such, I would expect a “quotation” mechanism to provide only the first functionality (allowing quoting a syntactic construct and thereby allowing a user to consume it as some kind of data), while a quasi-quotation to provide both functionalities (the prior, and also embedding expressions from the host language into the quoted data).

Looking at the documentation of both SwiPL’s quasi-quotations and Camlp5 quotations, they seem to effectively provide the same behaviour, with the main difference being just in terms of small subtleties in the interface they provide to the user. In particular, as Camlp5 quotations provide anti-quotations (i.e allowing embedding terms from the host language in the quoted text), they effectively also implement quasi-quotations.

That being said, if the following link is indeed the OCaml code in question, then it looks like it doesn’t really make use of any of the “quasi”-quotation functionalities provided by Camlp5, as the expander doesn’t actually use any anti-quotations.

Aside: a small plug - I maintain OCaml bindings to SwiPL that you might be interested in, if you’re doing something with OCaml + SwiPL: GitHub - Gopiandcode/SWIPL-OCaml: SWI-Prolog Bindings for OCaml: https://gopiandcode.github.io/SWIPL-OCaml/swipl/index.html

2 Likes

Thanks.

Still digesting what you wrote.


Since you noted the SWI-Prolog code and I found that you are on the Discourse site I bumped you to a regular user. :slightly_smiling_face:

1 Like

I maintain Camlp5, and was sitting next to DDR when he was writing Chamau original name of Camlp4], back in the day. So I have some familiarity with the roots of all this.

  1. All these quotations systems have their roots in LISP’s quotations and quasiquotations, just so as @Gopiandcode describes.
  2. I’m getting old (so the memories are fading) but IIRC the immediate ancestor was the quotation mechanism in the original LF ML, wherein a string enclosed in backticks (don’t know how to write it in Markdown, so not gonna try) was parsed using a special “term” parser.
  3. Daniel generalized this idea to arbitrary quotations, where quotations were named, and the syntax:
<:argle< .... text .... not containing gtgt ... >>

meant “invoke the quotation expander named ‘argle’ on this text”. And then (again IIRC) he took inspiration from antiquotations in LISP/Scheme[1] to add back in antiquotations, e.g.

<:expr< $a$ + $b$ >>
  1. In any case, you are right, that in some deep sense, SWI-Prolog’s quasiquotes are probably equipotent to Camlp5’s. Indeed, one could imagine using the new PPX string-extension syntax, viz. {%argle| ......|} (and {argle bar| ..... string not containing bar |bar} ) instead of <:argle< ..... >> and then one could imagine recursively-embedded quotations-within-quotations. And this wouldn’t even be too cumbersome, textually.

A key thing that makes parsing these quotations tractable, is that you can find the end of the quotation without parsing its contents – just look for “>>”. And this is the same for the string-extensions. And when parsing the contents of a quotation, when you find a “$”, you just scan forward for the next “$” – that’s where the antiquotation ends.

Key point being, you don’t have to parse the contents of either quotation or antiquotation, in order to know where it ends.

  1. But then the problem is how to embed anti-quotations within those quotations, without making things hopelessly cumbersome. What I mean is, imagine a quotation like
<:expr< $a$ + $b$ >>

which would be (in string-extension notation)
{%expr| $a$ + $b$ |}
and now imagine replacing the “a” with an expression like

let e0 = <:expr< x * x >> in <:expr< $e0$ * $e0$ >>

again, written using string-extension notation:
let e0 = {%expr| x * x |} in {%expr| $e0$ * $e0$ |}

Now combine those two bits of string-extension-laden text – you get something that’s both difficult-to-parse and difficult to even read.

I hope this is intelligible. The upshot of what I’m trying to say is that a quotation-that-contains-an-antiquotation-that-itself-contains-a-quotation-that-again-contains-an-antiquotation makes parsing pretty damn difficult.

[1] But I have a vague memory that Caml-Heavy (the original Caml on top of LeLisp) supported antiquotations, and maybe Daniel got his inspiration for that – it’s all a haze now.

ETA: the more I think about it, the more I am convinced that Daniel got a lot of his inspiration from Caml-Heavy’s quotations. I just don’t have any pointer to where I could find examples of that: it’s just a “feeling” based on vague and hazy memories. Sorry.

2 Likes

Just to confirm what @Gopiandcode and @Chet_Murthy have said.

While I have not used antiquotations with my translation at present (just getting started with the translation) and the handbook OCaml code uses default quotations (quotation without its name) here is a working translation.

OCaml

<<((1 + 2) + 3) + 4>>

SWI-Prolog

{|simple_expr_qq||((1 + 2) + 3) + 4|}

Both

  • use bookends unique to the programming language
  • accept strings in the (quasi)quotation
  • translate the string using a parser into an abstract syntax tree
  • only do the conversion at compile time.

The main difference is that for the SWI-Prolog version I explicitly use the name as I don’t see a way to set a default.

If I understand antiquotations correctly then SWI-Prolog does have them to some extent (at least the variables). In the predicate

call(+SyntaxName, +Content, +SyntaxArgs, +VariableNames, -Result)

one can see the variables names are there. As noted in the SWI-Prolog documentation

{|html(Name, Address)||<tr><td>Name<td>Address</tr>|}
1 Like

Just a little addition: perhaps you can point me at a place where Camlp5 supports
<<((1 + 2) + 3) + 4>> but geez, I don’t remember it being supported anywhere. OTOH, I could be completely wrong, b/c even though I’ve been maintaining Camlp5 and upgrading it for several years now, I certainly have not fully-understood everything DDR created. I thought you always had to specify the quotation-name. Again, I could be wrong about that.

Sure. Thanks for asking.

Since I am use to answering questions online all the time I know how much leaving out the simplest detail taken for granted can cause lots of time wasted for the one reading this to figure out what was meant so if this seems overboard you know why.

The source of the OCaml code I noted is from the Handbook available here.

It comes from intro.ml near the bottom. In order to make it easier to identify the lines there are several different GitHub repositories that have copies of the code. Using this repository the line can be uniquely identified.

Now to explain this a bit more.

As this code was originally written in ~2008 based on the book being published in March 2009 nowadays the code and examples would be more commonly be seen using an interactive notebook. In the handbook is the code example statements that are run from the OCaml toplevel. These are statement(s) are bookended by lines with START_INTERACTIVE;; and END_INTERACTIVE;;; don’t ask me how it works I still haven’t figure it out but don’t need too, or if I did a long time ago have long forgotten.

Now if we trace back on what happens to <<((1 + 2) + 3) + 4>> the first thing to know is that it is a Campl5 quotation. Since it does not have a ident (ref) it will use the default quotation.

This is were my knowledge is a bit sketchy as I don’t use Camlp5 quotations I only translate them.
In the Camlp5 documentation for defining a quotation by string it notes

Quotation.add name (Quotation.ExStr f);

where “f” is that quotation expander.

and in the handbook code (as also noted by @Gopiandcode) is

The use of default_parser is the connection to how the handbook sets up different default quotations.

Back to the code for

make_parser is a function for, you guessed it, making different parsers. The handbook makes different parsers for such types as simple expressions, first order logic and propositions.

The entire parser for simple expressions is in intro.pl

Hopefully that has enough detail to answer your question.

Note: I did not cover the creation of the printers as they don’t use Camlp5 quotations.

If you have more questions just ask. I may not be able to answer them but won’t know until I see them.

Note: The book explains most of this in more detail but still one needs the required background to read the book. The book is a bit expensive but is easily found using WorldCat. In the US if your library does not have the book consider using interlibrary loan.

1 Like

Hilarious! I’ve never exercise that code-path, and never seen it exercised in the examples, so I didn’t even know it existed! Ha! I’ll have to write some unit-tests for it!

Thank you for pointing it out!

1 Like

START_INTERACTIVE;; and END_INTERACTIVE;;
are converted to OCaml comments during the make process.

In doing some more research on the history of this with @Gopiandcode noting LISP went back and found more info. Some of this I knew some of it is new to me but after reading these understanding the LISP heredity now makes more sense.

  • “The Roots of Lisp” (pdf) by Paul Graham
  • “Quasiquotation in Lisp” (pdf) by Alan Bawden
  • “The Roots of Quotation” (with relation to R) (HTML) by Miles McBain
  • “Why It’s Nice to be Quoted: Quasiquoting for Haskell” (pdf) by Geoffrey B. Mainland
2 Likes