C style block -- is it a bad idea

Hello,

When writing a goal predicate I could use a c style block such as so:

predicate(X, Y) :-
   (
         p1(X),
         p2(Y)
   ).

I like this “block” style since it makes it more legible for me – but, would i be introducing some kind of overhead here?

thanks,

Dan

I’m almost sure that this is resolved by the SWI-Prolog parser. No
overhead here.

Volker

Maybe indenting would make it also legible, like this:

predicate(X, Y) :-
    p1(X),
    p2(X).

Altogether formatting code is a nifty idea, but obviously there are endless opinions on how to do it right.

yea.

That’s what i do, but i feel that with the block its even better for me.

Btw, still can’t figure out how to display code here in the forum in a pretty way.

The easiest way to do it is to

  1. On a line by itself enter three ticks ```.
  2. On the next lines enter the code. Copy and paste is easiest.
  3. On a line by itself after the code enter three ticks ```.

So if you are pasting in

predicate(X, Y) :-
   (
         p1(X),
         p2(Y)
   ).

in the editor it would look like this

```
predicate(X, Y) :-
   (
         p1(X),
         p2(Y)
   ).
```

If it makes it more legible for you, then why not?

I wouldn’t do it, just as I wouldn’t write (2+3) instead of 2+3 - it’s redundant. I would however write (2+3)*5 if I wanted to denote 25. In this case I’m forced to use brackets. This is a sort of analogy (using already familiar notions to explain the unfamiliar) and then again it isn’t (it is not two notions here but one: ‘+/2’ and ‘,/2’ are just operators as far as the Prolog parser is concerned).

1 Like

What aspect of

predicate(X, Y) :-
    (
        p1(X),
        p2(Y)
    ).

is more legible than

predicate(X, Y) :-
    p1(X),
    p2(Y).

?

(If it’s that you’re used to matching { and }, you’ll really hate Python and Haskell. :wink: )

For some predicates, the predicate name and args list is rather long, and the subgoal list includes lengthly predicates with args as well. This makes it hard to see whats going on.

So, subjectively, having a bit of a space while indicating “belonging” helps …

No need to follow this everywhere, but for some predicates i feel its helpful

Maybe if you give a few examples of predicates/subgoals that are hard to see what’s going on, people can make some restructuring suggestions …

A bit offtopic, but one example for redundant parentheses is given by Prolog itself:

?- B = 2 + 3, A = 1 + B.
B = 2+3,
A = 1+(2+3).

?- 

Repeating my claim in favor of a yfy-class for associative operators :slight_smile:

Why are these redundant? There’s no arithmetic happening; and I think you’ll agree that the term plus(2,3)) is different from plus(plus(1,2),3).

1 Like

Ok, you won. I still tend to forget that arithmetics needs to be explicitly requested. Wondering why I ever ended up with such a strange language.

There’s nobody winning or losing; just everyone gaining knowledge and insight. :wink:

A programming language needs to make a choice about how it evaluates things. I would suggest this breakdown:

  • implicitly eager eval – most languages (C++, Python, etc.)
  • implicitly eager eval but can be delayed – Lisp (with QUOTE, special forms, and LAMBDA)
  • implicitly lazy eval – Haskell
  • implicitly non-eval – Prolog

In addition, there can be syntactic sugar for these things, such as Lisp’s “special forms” (this is why you can write (if cond (do-something) (do-something-else)) instead of (if cond '(do-something) '(do-something-else)).

Prolog doesn’t need as much syntactic sugar as Lisp – there’s no need for “lambda”, for example, because you can just construct a term and then evaluate it later with call/1.(*)

One of the things that confuses many people learning Prolog is that a Prolog term is just the syntax for a data structure – e.g., plus(1,2) is nothing more than a cell named plus with two components: 1 and 2. It is not a constructor (in the C++ sense), it just is. Which means that one basic concept in Prolog is that if two things look the same, they are the same.(**)

The downside with Prolog is that it can sometimes be more verbose – if you call “write(1+2)” you’ll get “1+2” as output and not “3” … instead you need to do “V is 1+2, write(V)”. Some syntactic sugar has been proposed for this (IBM-Prolog used $ as an “eval” operator, as I recall), but there are subtleties because of Prolog’s unification rules (e.g., what does it mean if you apply the “eval” to an output argument, and how do you handle “eval” of a predicate that backtracks to multiple values).

Overall, I like Prolog’s design choices (if you want to see an ugly version of logic programming, look at the various implementations in Lisp); but it requires rethinking the meaning of some notation.

(*) call/1 can be thought of as being implicitly defined by:
call(foo(X)) :- foo(X).
call(bar(X,Y)) :- bar(X,Y).
etc.

(**): If portray/1 is used for displaying, then two things might look the same even if they’re different. But write_canonical/1 follows the “look the same, are the same” rule.

1 Like