HN user

triska

4,126 karma

Homepage:

https://www.metalevel.at

You are welcome, and you are welcome!

Posts150
Comments360
View on HN
github.com 25d ago

Kb – Prolog Knowledge Base

triska
109pts18
manzikert.ca 1mo ago

Conway's Game of Life in Prolog

triska
2pts0
github.com 2mo ago

A local-first multimodal knowledge platform for managing entities

triska
2pts0
vatmiraal.be 2mo ago

From Taxman to VATmiraal: Fifty Years of Teaching Machines the Law

triska
1pts1
github.com 3mo ago

Prolog Implementation of the IRS Fact Graph

triska
5pts0
github.com 3mo ago

Tax Logic Evaluation with Prolog

triska
2pts0
github.com 3mo ago

A simple online forum written in Prolog

triska
2pts0
github.com 3mo ago

Forth VM and compiler written in C++ and Scryer Prolog

triska
37pts0
github.com 4mo ago

A high-performance bridge between Scryer Prolog and Python

triska
2pts0
github.com 4mo ago

Simple Maturin Based Python Bindings to Scryer Prolog

triska
1pts0
github.com 4mo ago

A lightweight, embeddable Prolog interpreter written in C11

triska
4pts0
web.liminal.cafe 5mo ago

Blue Prince Clock Tower (CLP(Z), Scryer)

triska
3pts0
github.com 6mo ago

Pokemon Draft Tools in Prolog

triska
4pts0
web.liminal.cafe 6mo ago

Flowlog: An ISO-style Prolog system with faster search on multicore CPUs

triska
3pts0
github.com 7mo ago

A simple graph database implementation running on Scryer Prolog

triska
2pts0
github.com 7mo ago

Dogalog: A realtime Prolog-based livecoding music environment

triska
82pts21
github.com 7mo ago

Towards an Implementation-Independent Interface for Semantic Web in Prolog [pdf]

triska
2pts0
www.sciencedirect.com 8mo ago

Transforming legal texts into computational logic

triska
3pts0
news.ycombinator.com 8mo ago

Scryer Prolog library for Datomic: https://github.com/jjtolton/scrytomic

triska
2pts0
github.com 8mo ago

Python Integration for Scryer Prolog Using FFI (Research Project)

triska
2pts0
old.reddit.com 8mo ago

Stack based Prolog. Cool thing you can do with DCGs

triska
14pts1
osc.gov 8mo ago

Hatch Act Guidance on Social Media [pdf]

triska
4pts0
github.com 9mo ago

Global key-value metadata storage for Scryer Prolog

triska
13pts1
github.com 9mo ago

Neurosymbolic AI server combining Prolog's symbolic reasoning with MCP

triska
6pts0
github.com 9mo ago

Qupak: Pattern Matching for Prolog with library(reif)

triska
8pts0
github.com 9mo ago

Scryer Prolog v0.10.0 is out

triska
7pts0
prolog.pmikkelsen.com 11mo ago

PProlog: A Prolog for Plan 9

triska
18pts3
github.com 11mo ago

Take: Process file lines with a logic-based language

triska
3pts0
gitlab.com 1y ago

Semantic Web Interface for Prolog

triska
3pts0
arxiv.org 1y ago

Dose-Escalation Trial Protocols That Extend Naturally to Admit Titration

triska
2pts0

I think this is very well phrased, and I would argue the same holds for Prolog too.

In my opinion, a key difference between Prolog and other languages in that regard is one of degree, not kind: Compared to other languages, addressing performance problems in Prolog engines tends to have far greater effects on Prolog programs, because so much is implicit (i.e., left to the engine).

If the performance problem is not in the engine, but in the program itself, then we will face the same questions with Prolog as with other languages: How to formulate the program better, is there a better approach altogether?

For example, earlier today an interesting question regarding performance was posted in the Scryer discussions:

https://github.com/mthom/scryer-prolog/discussions/3341

The comparison in this case is between Gecode and Scryer on a seemingly simple but nontrivial combinatorial task. What is the problem here? Most likely the Scryer engine itself can be improved. And also very likely, there are better ways to model the task, and also better search strategies, and these tend to have far greater performance impact than the base language, and these questions remain also if we change the base language.

In my opinion, these questions regarding different kinds of formulations tend to be more frequently associated with Prolog than with other languages because Prolog is more frequently used for complex tasks where it is not a priori clear how to even approach the problem.

How is this different from other programming languages though?

One example I often think about is from Ken Silverman: "sub eax, 128" → "add eax, -128". So equivalent ways to write the same program may have different performance characteristics also depending on the tools that are applied. How many people could tell without trying which way to write this example is preferable?

The same phenomenon will be encountered in all kinds of languages, where engine and compiler improvements make existing code faster or slower.

Very nice!

In the Scryer Prolog discussions, Alex has shared a few ideas and considerations for possible improvements to the Prolog code, including the use of metaprogramming to automatically generate more general relations:

https://github.com/mthom/scryer-prolog/discussions/3221

I hope for an interesting followup article!

Another very interesting Prolog program by Alex is factgraph.pl:

https://github.com/alexpetros/factgraph.pl

It's a Prolog implementation of the IRS Fact Graph, an application of Law as Code.

Quoting from the Charter of Fundamental Rights of the European Union, https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:12... :

"Article 7

Respect for private and family life

Everyone has the right to respect for his or her private and family life, home and communications.

Article 8

Protection of personal data

1. Everyone has the right to the protection of personal data concerning him or her.

2. Such data must be processed fairly for specified purposes and on the basis of the consent of the person concerned or some other legitimate basis laid down by law. Everyone has the right of access to data which has been collected concerning him or her, and the right to have it rectified.

3. Compliance with these rules shall be subject to control by an independent authority."

On the other hand, how come that the desired connotation is not the immediately prevailing one in the land of the free which is not the land of no cost.

Privacy is also about having control over your own space, both physically and digitally, and being free from unwanted intrusion or interference.

For me, such a notification is an unwanted intrusion, and it is not compatible with privacy.

I think the article rightly speaks of "trust-erosion" in connection with this incident because, in addition to the showing of ads being subject to the suspicion of surveillance, it raises the question how seriously we can take a wallet app that shows ads or does anything completely unrelated to its designated and propagated purpose, something that is not the reason why this app is used and in fact detracts everyone from the intended use of this app.

The breakdown of trust is already in the question "What absurdity comes next from such a sensitive app?"

Quoting from https://www.apple.com/privacy/:

"Privacy. That’s Apple.

Privacy is a fundamental human right. It’s also one of our core values. Which is why we design our products and services to protect it. That’s the kind of innovation we believe in."

So, Apple explicitly advertises with privacy, which makes it very different from other big tech companies, and it seems justified to expect it to uphold its promise. "Privacy. That's Apple.", according to Apple.

I second this! What a fascinating read!

Regarding the point about current research, I found in the article:

"Gary Aston-Jones, head of the Brain Health Institute at Rutgers University, told me he was inspired by Putnam to go into neuroscience after Clarke gave him one of Putnam’s papers.

“Putnam’s nervous system model presaged by decades stuff that’s very cutting edge in neuroscience,” Aston-Jones said, and yet, “in the field of neuroscience, I don’t know anybody that’s ever heard of him.”"

Regarding the comment "13211-3 conformity approved" that appears in the thread:

This refers to the newly published Technical Specification (TS) of Definite Clause Grammars which are part of the Prolog standard since June 2025 via ISO/IEC TS 13211-3:2025:

https://www.iso.org/standard/83635.html

This standard was achieved thanks to the great cooperation between many experts over many years. Its publication is an important milestone in the development of Prolog, since this grammar mechanism can be rightly said to mark the beginning of Prolog, a programming language rooted in natural language processing tasks:

https://en.wikipedia.org/wiki/METEO_System

With recent Prolog systems such as Scryer Prolog and Trealla Prolog, also very large amounts of text can be efficiently processed with this formalism, using library(pio) to apply such a grammar directly to files.

for system settings specifically, I wonder what kind of ad targeting would you get out of that?

You get sensitive data out of system settings, such as for instance health data: Does the user have a vision or hearing impairment, use assistive technologies etc.?

As to other reasons apart from the violation of privacy: Every network call adds additional latency and slows down interactions with the OS. Every data gathering feature adds additional complexity to the implementation, takes attention away from other implementation work that could be done instead, and increases the risk of adding further mistakes to the implementation. Personally, I would like OS and application vendors to work on improving security and correctness of their programs and reducing latency instead of adding data gathering features.

But the line between a necessary network call and an optional one is often blurry.

What would be an example of a necessary network call that an ideal OS (i.e., one that cannot be easily compromised and does not require updates around the clock to correct programming mistakes) has to perform on its own?

If a company is interested in how users use their applications and desperately need our data for it, they may be interested in funding dedicated studies and appropriately compensating users that send their data, if it is so valuable for the company.

As relevant as ever, arguably more relevant than ever as more programs are being written and need to be adapted, in more and more complex domains.

Note what Naur means with Theory here. Quoting from the paper:

"What will be considered here is the suggestion that the programmers' knowledge properly should be regarded as a theory, in the sense of Ryle [Gilbert Ryle, The Concept of Mind, 1946]. Very briefly, a person who has or possesses a theory in this sense knows how to do certain things and in addition can support the actual doing with explanations, justifications, and answers to queries, about the activity of concern."

This is not "theory" in the sense we sometimes encounter in colloquial speech in the sense of (exclusively) "assumption", especially not with the connotation "unjustified assumption". It is also not a set of rules:

"The dependence of a theory on a grasp of certain kinds of similarity between situations and events of the real world gives the reason why the knowledge held by someone who has the theory could not, in principle, be expressed in terms of rules. In fact, the similarities in question are not, and cannot be, expressed in terms of criteria, no more than the similarities of many other kinds of objects, such as human faces, tunes, or tastes of wine, can thus be expressed."

Yet, it plays a central role in programming:

"For a program to retain its quality it is mandatory that each modification is firmly grounded in the theory of it. Indeed, the very notion of qualities such as simplicity and good structure can only be understood in terms of the theory of the program, since they characterize the actual program text in relation to such program texts that might have been written to achieve the same execution behaviour, but which exist only as possibilities in the programmer's understanding."

Very interesting, especially the mentioned use of the logic programming language Prolog as a scripting language, explained starting at 22:12.

Quoting from the talk:

"What kind of a language would I use? Any guesses? ... I wanted to try with Prolog. And why did I try with Prolog? [audience laughter] That's the thing: I want everything to be declarative, I really like declarative things. So, remember all those different configurations of Video4Linx? ... I want to not have to write all those algorithms myself. I want the device manufacturer, or the distro maintainer maybe, to describe what each of those devices does, or each of those nodes over here does, and I also want, on the other side, to describe what kind of an output I want ..."

There is also a scheduled talk about libobscura at FOSDEM 2025: https://fosdem.org/2025/schedule/event/fosdem-2025-6184-libo...

CLP(Z), which does not have an ISO standard

CLP(FD/Z) is a candidate for inclusion in the Prolog standard: Several Prolog systems provide it with notable commonalities in features, it fits perfectly into the existing language, and it follows the logic of the standard including its error system. It can even be implemented within Prolog, provided a few basic features are present in a Prolog system. For instance, the CLP(Z) system I provide and which is used in the paper runs with little modifications already in several different Prolog systems, including SICStus, Scryer and Trealla. CLP(FD/Z) is an admissible extension of the existing standard:

    5.5 Extensions

    A processor may support, as an implementation specific
    feature, any construct that is implicitly or explicitly
    undefined in the part of ISO/IEC 13211.
This is completely different from modifications of the standard that do not fit at all into the standard. For instance, interpreting double-quoted strings differently from what the standard prescribes is not an extension in the sense the standard defines it, but a modification of the standard.

In addition, Scryer Prolog has an execution mode where all its extensions are turned off. This is called a strictly conforming mode, and is also prescribed by the standard:

    5 Compliance

    5.1 Prolog processor

    A conforming Prolog processor shall:

      ...

      e) Offer a strictly conforming mode which shall reject
      the use of an implementation specific feature in Prolog
      text or while executing a goal.
In Scryer Prolog, the strictly conforming mode is the default execution mode.

Regarding the other points you mention: Even though it may sound easy to say "as long as that dialect has a reliable specification and implementation", I know no such system that exists, and what I see from systems that do not adhere to the Prolog standard makes me doubt that such a thing is possible. The systems that do not follow the standard often have elementary syntactic problems, such as reading a Prolog term that they themselves emit into a different Prolog term, a recipe for disaster and unacceptable in every domain I know.

the links you provided describe these applications without references to warranties and standards and regulation.

This is not true. For example, quoting from page 2 of the paper that is linked to in a discussion I posted, An Executable Specification of Oncology Dose-Escalation Protocols with Prolog, available from https://arxiv.org/abs/2402.08334:

"Standards are of great importance in the medical sector and play a significant role in procurement decisions, resolution of legal disputes, warranty questions, and the preparation of teaching material. It is to be expected that the use of an ISO-standardized programming language will enable the broadest possible adoption of our approach in such a safety-critical application area. For these reasons, we are using Scryer Prolog for our application. Scryer Prolog is a modern Prolog system written in Rust that aims for strict conformance to the Prolog ISO standard and satisfies all syntactic conformity tests given in https://www.complang.tuwien.ac.at/ulrich/iso-prolog/conformi...."

Regarding warranty guarantees of Scryer Prolog, may I suggest you contact its author if you need to negotiate arrangements that are not catered for by the only licence terms you currently have access to?

One important advantage you get from the strict syntactic conformance of Scryer Prolog is that it reliably tells you what is Prolog syntax and what is not. In this way, you can use it as a free reference system to learn what Prolog is. The conformance makes it easier to switch to other conforming systems, such as SICStus Prolog which also offers different licences and commercial support, when you need to.

The users in these super-sensitive domains don't seem as sensitive about them as you claim.

I am at a loss at this phrasing and also about the content of this text. Apart from the facts that I did not use the wording "super-sensitive", and that the importance of standards is explicitly stated in the paper I quoted above, is there even the slightest doubt about the great importance of standards when building and operating giant particle accelerators or devising dose escalation trials in clinical oncology?

A key performance attraction of Scryer Prolog is its space efficiency for representing lists of characters, yielding a 24 times (!) more compact representation than a naive implementation would.

With Scryer Prolog and other recent systems that implement this representation, such as Trealla Prolog, we can easily process many GBs of text with DCGs, arguably realizing the full potential of the originally intended use case of Prolog for the first time. Trealla Prolog goes even further already, and allows overhead-free processing of files, using the system-call mmap(2) to virtually map files to memory, delegating the mapping to the operating system instead of the Prolog system.

The linked benchmarks do not test these aspects at all, and in addition use a version of Scryer Prolog that was completely outdated already at the time the benchmarks were made: The benchmarks use Scryer Prolog v0.8.127, which was tagged in August 2020, more than 3 years (!) before the benchmarks were posted. The linked benchmarks thus ignore more than 3 years of development of a system that was at that time 7 years old. Newer versions of Scryer Prolog perform much better due to many improvements that have since been applied. More than 1700 commits were applied between these dates.

In the face of the 24-fold reduction of memory use that the above-mentioned efficient string representation enables, small factors of difference in speed between different systems are in my opinion barely worth mentioning at all in any direction.

And yes, in addition to this great space efficiency, the strong ISO conformance of Scryer Prolog is also a major attraction especially when using it in highly regulated areas. For example, here is a recently envisaged application of Scryer Prolog in the context of machine protection systems (MPS) of giant particle accelerators, where adherence to industry standards is of great importance for warranty reasons among others:

https://github.com/mthom/scryer-prolog/discussions/2441

As another example, a medical application of Scryer Prolog, in the highly regulated domain of oncology trial design:

https://github.com/mthom/scryer-prolog/discussions/2332

Here is an overview of syntactic ISO conformance of different Prolog systems:

https://www.complang.tuwien.ac.at/ulrich/iso-prolog/conformi...

Codd's seminal paper, A Relational Model of Data for Large Shared Data Banks, states that a language based on applied predicate calculus "would provide a yard-stick of linguistic power for all other proposed data languages". Quoting from https://www.seas.upenn.edu/~zives/03f/cis550/codd.pdf:

"1.5 Some linguistic aspects

The adoption of a relational model of data, as described above, permits the development of a universal data sub-language based on an applied predicate calculus. A first-order predicate calculus suffices if the collection of relations is in normal form. Such a language would provide a yard-stick of linguistic power for all other proposed data languages, and would itself be a strong candidate for embedding (with appropriate syntactic modification) in a variety of host languages (programming, command- or problem-oriented)."

Languages based on predicate calculus indeed seem extremely suitable for reasoning about relational data. Datalog is a well-known example. It is more directly based on predicate logic, and much simpler than SQL.

Regarding relational algebra in particular: It is interesting that important and frequently needed relations on graphs cannot be expressed in relational algebra. The transitive closure of a relation is a well-known example, and as you nicely show in your article this relation can be easily and very naturally expressed in two lines of Datalog. For example, we can easily express reachability in a graph:

    reachable(V, V) :- vertex(V).     
    reachable(From, To) :-            
            arc_from_to(From, Next),  
            reachable(Next, To).      
One can show that Datalog with two very conservative and simple extensions (allowing negation of extensional database relations, and assuming a total order on the domain elements) captures the complexity class P, so can be used to decide exactly those properties of databases (and hence graphs) that are evaluable in polynomial time, a major result from descriptive complexity theory.

An example of such a property is CONNECTIVITY ("Is the graph connected?"), which can be easily expressed with Datalog on ordered databases, where we assume 3 built-in predicates (such as first/1, succ/2 and last/1) to express an ordering of domain elements:

    connected(X) :- first(X).
    connected(Y) :- connected(X), succ(X, Y), reachable(X, Y).

    connected    :- last(X), connected(X).
If such an ordering is not available via built-in predicates, then we can easily define it ourselves for any given concrete database by adding suitable facts. Also negated EDB relations can be easily defined for any database as concrete additional relations.

Very interesting! Let's consider for example the definition of sum_list/2 which is shown in Fig. 1, Fig. 2 and Fig. 3:

    %% sum_list(+Number_List, ?Result)
    % Unifies Result with the sum of the numbers in Number_List;
    % calls error/1 if Number_List is not a list of numbers.
    sum_list(Number_List, Result) :-
            sum_list(Number_List, 0, Result).
    
    % sum_list(+Number_List, +Accumulator, ?Result)
    sum_list([], A, A). % At end: unify with accumulator.
    sum_list([H|T], A, R) :- % Accumulate first and recur.
            number(H),
            !,
            B is A + H,
            sum_list(Rest, B, R).
    sum_list(_, _A, _R) :- % Catch ill-formed arguments.
            error('first arg to sum_list/2 not a list of numbers').
Compiling it with Scryer Prolog, I get a warning:
    $ scryer_prolog sum_list.pl
    Warning: singleton variables T, Rest at line 4 of sum_list.pl
       true.
A singleton variable often indicates a mistake in the code. And indeed, the sample code uses Rest where it apparently meant to use T (or vice versa). So, I change the second clause of sum_list/3 to:
    sum_list([H|T], A, R) :-
            number(H),
            !,
            B is A + H,
            sum_list(T, B, R).
And now we're ready to use the predicate! Let's ask Prolog the most general query: Which answers are there in general? So, a query where all arguments are logic variables:
    ?- sum_list(Ls, R).
       Ls = [], R = 0
    ;     error(existence_error(procedure,error/1),error/1).
The existence error is due to the use of the non-standard predicate error/1 in the code sample. The predicate apparently meant to throw an exception telling us:
    first arg to sum_list/2 not a list of numbers
But the query did not restrict the first argument at all, so it may just as well be a list of numbers! The predicate probably meant to say that the argument is not sufficiently instantiated. In that case, it should have thrown an instantiation error. The standard predicate (is)/2 throws such an instantiation error for us in such cases. Also, it throws type errors for us! A type error is categorically different from an instantiation error: From a logical perspective, a type error can be replaced by silent failure, but an instantiation error can not.

We can therefore write the second clause as:

    sum_list([H|T], A, R) :-
            B is A + H,
            sum_list(T, B, R).
and also remove the third clause entirely. We now get:
    ?- sum_list(Ls, R).
       Ls = [], R = 0
    ;     error(instantiation_error,(is)/2).
From a logical perspective, that's OK: The predicate tells us that too little is known to make any statement, and a more specific query may yield solutions.

Also, this version correctly distinguishes between type and instantiation errors, and we now get for example:

    ?- sum_list("abc", R).
       error(type_error(evaluable,a),(is)/2).
As I see it, a key attraction of logic programming is that we are able to reason logically about our code. This holds as long as certain logical properties are preserved. The paper hints at such properties for example with the concept of steadfastness, which it defines in Section 5.1: A predicate "must work correctly if its output variable already happens to be instantiated to the output value". How can we tell though which variables are output variables, and also why even distinguish a particular variable as "output variable"? Should this not hold for all variables?

A particularly important logical property is called monotonicity: Generalizing a query (or program) can at most add solutions, never remove them. With monotonic predicates, debugging is very nice: For instance, if a predicate unexpectedly fails, then we can generalize it by removing goals, and if the remaining fragment still fails unexpectedly, then there must be a mistake in that fragment. Scryer Prolog provides library(debug) for this approach of declarative debugging:

https://www.scryer.pl/debug

In Scryer Prolog, we can define the specific case of arithmetic over integers with constraints over integers, for example like this:

    :- use_module(library(clpz)).
    :- use_module(library(lists)).
    :- use_module(library(lambda)).
    
    list_sum(Is, S) :- foldl(\I^S0^S^(S #= S0 + I), Is, 0, S).
Higher-order predicates such as maplist/N and foldl/N retain logical properties of the predicates that occur as arguments.

The most general query now works as expected:

    ?- list_sum(Is, Sum).
       Is = [], Sum = 0
    ;  Is = [Sum], clpz:(Sum in inf..sup)
    ;  Is = [_A,_B], clpz:(_A+_B#=Sum)
    ;  Is = [_A,_B,_D], clpz:(_A+_B#=_C), clpz:(_C+_D#=Sum)
    ;  ... .
The predicate does not terminate, as expected, because we expect solutions for lists of all lengths:
    ?- list_sum(Is, Sum), false.
    loops.
And other cases are simply specific instances of the most general query:
    ?- list_sum([1,2,3], Sum).
       Sum = 6.
Note that I have changed the predicate name from sum_list/2 to list_sum/2, because the list is the first argument, and the sum is the second argument. So, I am now using "sum" no longer as a verb, but as a noun, because that seems more appropriate for code that is declarative, not imperative: We describe what is true, not what must be done, and our code works in all directions and also with different execution strategies. integers_sum/2 may be an even better name in this case.

One other naming convention I like to use is to append an "s" for logic variables that stand for lists, such as "Is" for a list of integers.

With Prolog, we can often very naturally map such puzzles to programs, or at least to declarative descriptions that can be easily interpreted by Prolog programs.

For instance, in this concrete case, with a suitable operator definition for the operator says, we can write:

    :- op(800, xfy, says).

    solution([A,B,C,D,E,F,G]) :-
            G = salesman,
            E = salesman,
            C says D = engineer,
            A = engineer,
            A says B says C says D says E says F says G = engineer.
It is then left to interpret the statements, which we can do for example with:
    :- use_module(library(dif)).

    engineer says Stmt :- false(Stmt).
    salesman says Stmt :- true(Stmt).

    false(A = B) :- dif(A, B).
    false(engineer says Stmt) :- true(Stmt).
    false(salesman says Stmt) :- false(Stmt).

    true(A = A).
    true(engineer says Stmt) :- false(Stmt).
    true(salesman says Stmt) :- true(Stmt).
Yielding:
    ?- solution(S).
       S = [engineer,engineer,engineer,salesman,salesman,salesman,salesman]
    ;  S = [engineer,salesman,engineer,salesman,salesman,engineer,salesman]
    ;  S = [engineer,engineer,salesman,engineer,salesman,salesman,salesman]
    ;  S = [engineer,salesman,salesman,engineer,salesman,engineer,salesman]
    ;  false.

The article suggests using Boolean logic, so let's apply it: One way to solve this is to introduce a Boolean variable for each of A,...,G, and to use 1 to denote that a statement is true, and also to denote that the corresponding person tells the truth. It then remains to relate the truth of each statement to the truthfulness of the person making the statement.

In Prolog, we can express these relations with CLP(B), constraint logic programming over Boolean variables:

    ?- use_module(library(clpb)).
       true.
    ?- sat(G),
       sat(E),
       sat(C =:= ~D),
       sat(A =:= (B =:= (C =:= (D =:= (E =:= (F =:= ~G)))))),
       sat(~A),
       labeling([A,B,C,D,E,F,G]).
Yielding 4 solutions that satisfy all constraints:
       G = 1, E = 1, C = 0, D = 1, A = 0, B = 0, F = 1
    ;  G = 1, E = 1, C = 0, D = 1, A = 0, B = 1, F = 0
    ;  G = 1, E = 1, C = 1, D = 0, A = 0, B = 0, F = 1
    ;  G = 1, E = 1, C = 1, D = 0, A = 0, B = 1, F = 0.
From this, it is clear that there are 3 engineers, in all possible situations consistent with the description.

If we omit the labeling/1 goal which enumerates all solutions, then we get a symbolic representation of all remaining constraints:

    G = 1, E = 1, A = 0, clpb:sat(C=:=D#B#F), clpb:sat(C=\=D).
From this, it is clear that there are at least 2 engineers in every solution: A (as stated in the description of the puzzle), and either C or D (but not both).

Tested with Scryer Prolog.

This is also how Prolog terms are represented on the heap in the Warren Abstract Machine (WAM). For instance, taking the example of the article, if we have an expression such as the Prolog term +(*(a,b), c), written using operator notation as:

    expr(E) :-
            E = a*b + c.
Then we get a flattened representation on the global stack of the virtual machine. In Scryer Prolog, we can inspect the WAM instructions with:
    ?- wam_instructions(expr/1, Is),
       maplist(portray_clause, Is).
yielding:
    put_structure(*,2,x(3)).
    set_constant(a).
    set_constant(b).
    put_structure(+,2,x(2)).
    set_value(x(3)).
    set_constant(c).
    execute(=,2).
Note how both compound terms are linearized, and appear on the heap as: functor, followed by arguments, each occupying exactly one memory cell of the WAM. The arguments can point to other memory cells. The heap is an array of such cells, all of the same concrete (as opposed to abstract, i.e., WAM-level) type. For example, Scryer Prolog uses 8 bytes for each cell, making cell access and modification very efficient on 64-bit architectures.

Djot is the markup syntax that is used for the documentation of Scryer Prolog, using a parser written in Prolog:

https://github.com/aarroyoc/djota

It works well so far. One of the few limitations I noticed so far pertains to the formatting of tables. For instance, consider the table used in library(format) to describe control sequences:

https://github.com/mthom/scryer-prolog/blob/b0566e41503a6c8d...

It contains several entries that span multiple lines, yet are meant to denote only a single row of the table, such as:

    % | `~Nr`    |  where N is an integer between 2 and 36: format the            |
    % |          |  next argument, which must be an integer, in radix N.          |
    % |          |  The characters "a" to "z" are used for radices 10 to 36.      |
    % |          |  If N is omitted, it defaults to 8 (octal).                    |
It appears that Djot requires to write the entire entry in one long line, otherwise it gets formatted as shown currently at:

https://www.scryer.pl/format.html

From a readability perspective in the source file itself, a very long line is suboptimal. Maybe there is a way to retain optimal readability in the source file, and still get the intended rendering?

As I see it, the result is rather establishing a very fundamental property pertaining to the expressive power of a mechanism, and it can be useful also in practice.

For instance, I have many potential applications of Turing complete formalisms, because I am interested in results of arbitrary computations. The result obtained in the article means that I can use a Neural Network to obtain this, under the conditions outlined in the article, and in the way shown in the article.

This may simplify software architectures, especially in situations where Neural Networks are already applied, and additional mechanisms would otherwise be needed to cover arbitrary computations.

Quoting from the article:

"To prove this we assume that internal activations are represented as rational numbers with arbitrary precision."

And:

"Transformers with fixed precision are not Turing complete."

It will be interesting to see if this leads to better support for arbitrary precision arithmetic in processor architectures.

Personally, I always found that the novel ends on a comparatively hopeful note, especially given the circumstances, the final word being cudów ("of miracles").