Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

The arrow uses a cut internally which renders it nonlogical and ruins any benefit you'd get from even writing Prolog. At that point you're just using Prolog as a quirky imperative language.

Unfortunately since it's almost impossible to model ideas without stuff like the arrow, cut, or \+ (not) operators, writing actual pure logical Prolog programs that actually do interesting things is supremely difficult.



So the arrow doesn't represent logical implication? As a non-native, I would have read that as a clause that always resolves to `baz(X, Y)` unless the implication holds (i.e., `some_condition(X)` is true). Isn't that logically consistent?


In general, using any of the mentioned predicates (->)/2, (\+)/1 or !/0 in Prolog code destroys essential properties we expect from classical logic, and therefore complicates declarative reasoning about the code to such an extent that it can be said to prevent it entirely.

For example, both versions of foo/2 that are shown above suffer from the same core deficiency: If some_condition/1 has multiple solutions, then they are cut away and not generated at all. Therefore, for the most general query,

    ?- foo(X, Y).
only a single answer, or a proper subset of all answers one would expect when reading (->)/2 as "implies", may be shown where, under a declarative reading based on first-order logic, there ought to be more.

This is problematic because if any concrete solution S is cut off due to this deficiency, we can still force the Prolog system to yield it with:

    ?- X = S, foo(X, Y).
So, we are in the situation that adding a constraint (X = S) yields a solution that is not shown when the constraint is removed. In other words, a specialized version of the predicate is more general than the original definition. This violates monotonicity, which is an essential property of classical first-order logic, and the basis for declarative debugging approaches that let us reason about the location of mistakes in Prolog predicates.

To write good declarative Prolog code that is also efficient, we first need to find and implement the mechanisms that let us express predicates like foo/2 in such a way that they are efficient while retaining their generality as logical relations.

One important building block pertaining to this concrete example was recently found with the new language construct if_/3, described in Indexing dif/2 by Ulrich Neumerkel and Stefan Kral:

https://arxiv.org/abs/1607.01590

With if_/3, we can write foo/2 as:

    foo(X, Y) :-
        if_(some_condition(X),
            bar(Y, X)
            baz(X, Y)).
The only requirement for this to work is to have a reified version of some_condition/1, in such a way that we can tell, from the implicit second argument, whether the condition holds or not, while still generating all answers for the most general query.

Some predicates are already reified in the library, so we can write for example:

    ?- X = a, if_(X = a, Y = b, Y = c).
    X = a,
    Y = b.
which succeeds without leaving choice-points, and that is good for performance. At the same time, we get both answers (X = a and also dif(X, a), i.e., X is not equal to a) on backtracking if we omit the first goal:

    ?-        if_(X = a, Y = b, Y = c).
    X = a,
    Y = b ;
    Y = c,
    dif(X, a).
Hence, the predicate retains important logical properties we expect from first-order logic, and this allows declarative reading and debugging based on these properties.

To benefit from the true power of Prolog, such constructs must become available in Prolog implementations. For example, currently, the only Prolog system that ships with if_/3 as a built-in feature is Mark Thom's Scryer Prolog, where if_/3 and related predicates are available in library(reif):

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

https://github.com/mthom/scryer-prolog/blob/master/src/prolo...

Other features that are necessary to make Prolog programs more declarative include CLP(ℤ) constraints, pure I/O, efficient implementation of partial strings as lists of characters etc., all of which are currently work in progress and as of yet only partially available in some Prolog systems. Over time, such features will become more widely available, and then they can be more easily taught and used to obtain Prolog programs that are pure and efficient.


As someone who had a prolog class last year and who really had a hard time understanding cut, this clears up a lot. Essentially, we were taught to write predicates without cut, and if they didn't yield proper results to introduce cut higher and higher up the predicate tree until they did.

I always felt that the language was more incomplete for having (needing) such an imperative construct in an otherwise entirely declarative design.


Thank you for such a complete answer! This was very enlightening.


I love pure Prolog, and I welcome new developments like the pure if_/3 discussed below. But at the same time, your position is needlessly extremist. It's perfectly fine to write a program in 90% pure Prolog and 10% in its "quirky imperative" subset. Depending on what you're doing, turning that ratio around to 20-80 can still be useful: "quirky imperative" Prolog is still the best language for matching and transforming tree-shaped data that I know of.

The ability to mix in impure features where needed is what makes Prolog practical.


The problem is, once you introduce a non-logical predicate, everything in the call stack "above" it is tainted and rendered non-logical. Since you frequently have to resort to using (\+)/1 almost as a necessity, the codebase ends up being 90% illogical and 10% logical. And at that point you step back and ask why you're even using prolog.

However: I do miss DCGs in every other language I've used. They're so useful that I've come to hate using regexes or even worse: manually parsing lists and strings.


> The problem is, once you introduce a non-logical predicate, everything in the call stack "above" it is tainted and rendered non-logical.

That's a very general statement. The non-logical behavior can be well-contained and unobservable from the outside. For example, the nice and pure if_/3 internally uses both of the non-logical predicates (->)/2 and (==)/2. Yet viewed from the outside it doesn't taint anything.

> And at that point you step back and ask why you're even using prolog.

As I wrote above, because I think even used imperatively it's a very convenient language for expressing matching and transformation of trees. And DCGs are great as well, I agree.


Very well said, exactly my opinion as well.


> nonlogical

'Logical' is a misnomer, Prolog is really a database query language. Unlike SQL it's not relational and not based on joins, it has tree walking semantics.

That said, without a decent storage/indexing system under the hood it is indeed quite pointless. Nobody wants a 'small data' tiny DB engine in 2019.


This is a good point and syncs with our approach: https://terminusdb.com/docs/woql




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: