Skip to content
cosmos-lang edited this page Sep 4, 2023 · 3 revisions

LogicScript is a revised syntax for Prolog-style languages.

p(x) <-
  q(y)
  z(c)
q(a)
z(a)

What is it?

LogicScript is simply Prolog with a syntax that one may find more amenable.

For example,

  • It's whitespace-sensitive. This makes it easier to switch clauses around.
  • The <- operator is used. This makes the code more readable in some ways.
  • Modern arithmetic operators are used, e.g. != instead of \==.

This redesign is made taking into account what Prolog is,

  • As a tree-processing language.
  • As a language for programming in logic.
  • As a language for both pure and impure logic programming.

One might try to change Prolog by adding higher-orderness (akin to functional programming), turning it into a pure logic language and so on. As this is already done by other languages, including one of our own, the aim of LogicScript is simply to provide an amenable syntax for Prolog itself, that may be then converted into Prolog by a transpiler or otherwise.

It doesn't try to drastically change what Prolog is, instead opting to keep it as-is.

One might say these changes are meant to emphasize its Good Parts™.

Why?

Starting off, LogicScript uses the <- operator. That is, we use an arrow.

mortal(X) <- human(X)

It's an obvious choice. The above code sample is meant to be read as,

If X is a human, X is mortal.

  • If we look at a typical sample from a Prolog tutorial or the likes, it's meant to be read as a conditional.
  • The arrow makes it immediately evident that it's a reverse conditional. "If X is a human, then..."
  • It emphasizes that we are programming in logic, as it's a logic operator (as opposed to meta-logic).
  • The creators of Prolog themselves will use arrows (Prolog III, KELPS). Likely due to the above.
  • Other programmers will gravitate to a similar syntax when given the chance (lps.js).

In summary, everyone already reads it that way and besides, it looks good!

Prolog: the Good Parts™

Prolog has many questionable design decisions likely kept only because of backwards-compatibility.. to a decades old language. Everything about it is simply incongruous and it looks terrible.

  • The programmer has to switch to decades old operators to program a predicate.
  • The operator not is deprecated for not being a pure logic operator. Therefore, it's deprecated...and changed to a \+! Programmers then use the impure \+ instead. That...has never helped anyone and doesn't solve anything whatsoever.
  • On the other hand, the operator is/2 is kept. This is despite being an impure operator in a logic language where = exists.

Despite this, it's not all bad. The language does have its strengths. Let's call it the language's Good Parts™*.

Note: we do not have copyright over the words "Good Parts".

How Prolog was Envisioned

As a Tree-processing language

Suggested source: Prolog in 10 figures

Prolog is seen as a tree processing language. A functor plus(1,2) is a tree that has the terms 1 and 2 as its leafs.

Once again, let's look at is/2.

X is 1+Y

This makes sense once we understand that 1+Y is a shorthand for the functor +(1,Y) that is evaluated by is/2.

However, this also gives odd results for a logic language.

:- 2=1+1
| false

The decision to have is/2 operate on a tree makes sense...if you ignore how it pretends not to be a tree, how the operator could be better named or how it gives the result 2=1+1 in a logic programming language (contrasting with 3). In hindsight, I'm not too sure about this operator.

As Programming in Logic

Logic programming has been described by the pseudo-equation Algorithm=Logic+Control.

A logic program is composed of logical statements and a way with which to interpret them. Both the logical and procedural parts make up logic programming.

As a Tool to explore Pure logic programming

Both pure and impure programming

Therefore, we propose a few simple changes. These changes take into account how the language is used both for pure and low-level programming.

Pure logic programming

A syntax for constraint arithmetic has already been proposed.

{X=1+Y}

We find this acceptable. Therefore, we simply extend it to other constraint systems,

fd:{X=1+Y}

It's easy to switch from a constraint system to the other. This uses the constraint system for finite domains, i.e. integers.

is:{X=1+Y}

Finally, the is module changes it back to regular ISO arithmetic.

Low-level (impure)

LogicScript also offers sugar intended for low-level (impure) programming.

case:
p(x) <- var(X), do_this.
p(x) <- is_list(X), do_this.
p(x) <- is_assoc(X), do_this.

This compiles to,

p(x) :- var(X), !, do_this.
p(x) :- is_list(X), !, do_this.
p(x) :- is_assoc(X), !, do_this.

This is a typical pattern where one uses the ! operator to select on type. It's akin to a switch-case pattern. Often, it's first tested whether the argument is a variable.

Prolog programming often involves low-level (impure) programming. These sometimes involve a pattern, in which case they can be shortened.

delay(X):
p(X) <- is_list(X), do_this.
p(X) <- is_assoc(X), do_this.

Other directives are possible. delay waits until the variable is ground before calling the predicate. This is a more high-level approach.

Explanation

Here we use the tried-and-true Good Parts™ approach.

  • It's explained how a language is, in all honesty, completely bonkers.
  • It's explained how, despite this, the language is actually amazing.
  • (Optional) We then make a proposal. This often takes the form of a linter or transpiler.

In doing this, we didn't fundamentally change the language. Anyone skeptical might say we achieve nothing of note whatsoever. After all, all we only rambled about a language's syntax. Although maybe you learned something. Hopefully.

Nonetheless, this is all also part of the Good Parts™ approach. At the end of the day, do you really want to program in a language from the 80's without using superior arrows? Well, me neither! Thus, our endeavor was a success.

Clone this wiki locally