Delightql and Operational and Denotational Semantics
This discussion treats operational semantics as meaning arising from explicit, stepwise evaluation, and denotational semantics as meaning arising from identification with essence. Where operational semantics is concrete, denotational semantics is platonic. Where operational semantics constructs (compositional in evaluation), denotational semantics proffers (atomic in identity).
Using these two different approaches as a framework, we can highlight certain delightql features that exist closer to each pole. Namely, Delightql’s left-to-right evaluation and scoping order as an operational semantics, and the use of functor syntax for opaque and/or non-finitary predicates as an explicit marriage of syntax to denotational semantics.
Denotational Functors
Delightql, like Prolog, assigns functor notation to the realm of relations, i.e. subsets of cartesian products that can be viewed equally as data (tables or views) or as evaluative oracles to membership and existence (predicates).
In Prolog, the functor stands for itself, denotationally, but also provides operational semantics via SLD resolution from which extensional values may be calculated. Granted, Prolog has a complicated relationship with anything that is not a term within its axiomatized Herbrand universe. This results in a awkward operational grafting of very practical things like numbers and their predicates which, while denotationally of the same kind (cartesian subsets and functions of truth), are operationally hopeless. Denotationally we could posit the existence of a <(X,Y) (i.e. X<Y) which satisfies our intuition about this predicate, but we have no feasible or useful way to either store infinity or build up numbers inductively via Peano style recursion. Nor should we. Prolog merely chooses to state that some predicates are black-boxes and built in – to wit, this is valid Prolog:
adult_user(Name,Age) :- user(Name,Age), >(Age,20).
albeit, with moded restrictions that prevent trying to query infinity. Delightql follows this syntactic consistency.
In contrast, in Sql predicates and relations are two entirely different beasts. Predicates are denizens of the WHERE-clause and always built-in, and relations are the user-defined tables and views of the FROM-clause. Delightql does not follow this syntactic inconsistency.
Lest this seem too grandiose, Delightql is just a SQL transpiler, but it helps to have the first crack at a programmer’s attention and imply a grand-unified theory of predication via syntax.
In the sense that all functors denote predicates regardless of their finite/data/observable or infinite/black-box/escape-hatch nature, Delightql is trying to be as consistent as Prolog and more consistent than Sql.
Any syntax of the form
foo(x,y,z)
denotes a predicate – a container for members, a cartesian subset, a truth-function of membership, or an operational black-box for divulging any subset of dimensions via querying – all equivalent.
Practially, the functor notation could be any of the following:
- an actual table of stored data
- a view calculating data from other views or tables
- a programmer composed predicate (not present in SQL, but available within delightql)
- an opaque built-in provided by the underlying engine (like
like(last_name, "%son")or>(age,2))
That delightql unifies these via syntax means we are appealing to denotation for this corner of our language. The consequences of this extend to composition via conjunction, i.e. the syntactic distinction between a join and a where clause is minor because a JOIN and a WHERE clause are denotationally equivalent.
Operational Semantics and Scope and Evaluation Order
On the other hand, Delightql makes a very strong appeal to operational semantics in how it asks the programmer (and the program) to understand meaning for order of operations and scope both. Delightql borrows from other paradigms: array languages, concatenative languages, and stack languages, in this decision: a snippet of code is meaningful as long as its resources (variables) have already been mentioned or come into scope, but not before.
While, in Prolog, a declative rule like
young_user(Name,Age) :- user(Name,Age), Age < 20.
could be re-written via commutation around the comma (conjunction) as
young_user(Name,Age) :- Age<50,user(Name,Age).
in delightql, the equivalent of the second Prolog form would be meaningless. The operational procedure of consuming text from left to right means that at the point of seeing age<50 we have no grounded source to provide meaning.
young_user(name,age) :- age<50 // the expression CANNOT be continued from here...
This restriction of meaning forces conceptual simplicity at the expense of being a bit odd. This oddness is, of course, relative: to the majority of the programming community, unfamiliar and extra work; to others, simple and easy enough to get used to.
Lost in delightql’s approach is the familiar utility that regular languages provide: forward references, implicit order of operations, and a general sense of convenience (albeit founded on implicit rules).
There are some interesting consequences of this choice:
- if it parses, it means something
- the parsed prefix (left) awaits a continuation of the expression and becomes the input to that continuation
- some SQL-isms become unnecessary (
HAVINGmostly) - implementation is simplified – since scope is positional, the compiler never need guess at references
Delightql also has one extra prohibition: there is no escape hatch via parentheses.
It is worth reiterating how operational this decision is. There is a very simple operational algorithm for deciding scope and application order that is crucial in defining meaning. This operational semantics unwinds Sql’s opaque/implicit order of operations into a mental stack and one memorized rule. It is not, however, as convenient as Prolog and many other declarative languages.
Calling back, we have two traditional approaches to semantics from which to analyze our language-design. Delightql chooses both for different purposes: denotational semantics for conceptual unity, and operational semantics for executive clarity.
In the former, denotational semantics allows us to recognize the equality of all predicates’ essence regardless of finitary/calculable considerations. This recognition is manifest as a language choice: that like kinds will share like syntax. It serves no other purpose than to shape the end-user’s approach to the underlying concepts, though this is not nothing.
In the latter, delightql takes direct aim at Sql’s Gordian evaluation order by appealing to operational considerations: “One rule, left to right, and the meaning follows.”
Author’s Notes
The idea for delightql started in 2017.https://old.reddit.com/r/programming/comments/5hbu9x/naga_datalog_based_rules_engine_in_clojure/daz6r2e/ The book was written starting in 2020 through to 2025.
Various proof of concepts were written between 2017 and 2020 before all effort was placed only into writing the book. The author believed that with a good enough reference, the language would insist on existence.
In mid 2025, coding began on the specifications listed in the book.
The language was originally named datalogite for its obvious datalog inspiration. Its name changed to dliteql but soon clashed with the opensource project dlite. Rather than give up on this train of thought, the author switched to the homophone: delightql.
The Datalog Education System predates delightql significantlyThe first release of DES was 2003 and makes the same connection that underpins all of deligtql: SQL and logic programming are the same. Other SQL transpilers have made similar connections. See: Logica. Many of the ideas around tree-groups found their inspiration from the COGROUP operator in pig latin and from other ‘big data’ relational technologies that were permissive to zeroth-normal form data.
There are other influences in the syntax, the most obvious one being the pipe operator |> via Elixir and before that ML and F#. Syntax aside, pipelining has been an obvious composition technique with job control in UNIX being the glaringly obvious origin.
The author was always humbledIn the ‘cut you down to size’ sense. to discover that many of the ideas or syntax he thought he invented had precedent. In a lot of cases, these might have been latent memories, but at least in one case it felt like obvious co-invention based on obvious principles.See the usage of ; for OR in a fact functor which is also used by AnswerSet programming.
Colophon
Design:
This document uses the following faces:
Quicksandfor pargraph contentiMWritingMono Nerd Font Propofor code content
To thank both for the document and the language implementation:
- typst
- pandoc
- jj
- mise
- uv
- rust
- tree-sitter
- neovim
- UTM
- apple silicon
- claude
- zotero