2

I am currently using SWI-Prolog to write some code. When I used Prolog years ago I could swear I was able to set variable names to "deconstructions" in the clause head, but it doesn't seem to work correctly for me now (at least in SWI-Prolog).

Toy stupid example:

    example(X = [Row|Rows]) :-
      do_something_with_x(X), 
      do_something_with_row(Row),
      ...

I want to call it as e.g. example( [1,2,3] ) and I want both X and [Row|Rows] to be matched against the same predicate's argument, namely the first argument (here, [1,2,3]), in the predicate's body.

Does anyone know if this is possible?

false
  • 10,264
  • 13
  • 101
  • 209
user1992634
  • 764
  • 7
  • 16
  • 2
    No, you cannot. Clauses' head are **unified** with goals (clauses bodies), and unification is what you call `deconstructions` and would invoke with `=` – CapelliC Mar 27 '19 at 06:49
  • @CapelliC I honestly do not understand what you are trying to say :-( I don't know if it is a grammar thing or a different problem. – User9213 Mar 27 '19 at 07:39
  • 2
    Sorry the explanation is not that clear, but the meaning should be understandable. Anyway, it's not forbidden syntax, but it doesn't what you're after. Deconstruction (**and** construction) are **both** subsumed by unification, that is **always** performed on the head - so called pattern matching. You have the `(=)/2` **call** to perform explicit unification as a goal. – CapelliC Mar 27 '19 at 08:00
  • 1
    @CapelliC I understand now. But I understand because I understand the vocabulary you are using and I know already what you are trying to explain. I suspect that OP might have trouble with that. – User9213 Mar 27 '19 at 08:52
  • @CapelliC *"... and you could well have defined the `=/2` yourself simply as `=(A,A).`."* :) – Will Ness Mar 27 '19 at 09:10

3 Answers3

5

Indeed this is the functionality that is missing in Prolog (in Haskell, it is known as "as-patterns"; in foo( x@(a:b) ) = ... x refers to the whole while a and b refer to its parts).

Closest thing you could do is

pred(X) :- X = [A|B], 
    ... .

but also,

pred(X, [A|B]) :- 
    ... . 

while always calling it with the same thing used twice: Z = ... , pred(Z, Z), ....

Continuing this idea, you could actually define

pred( X = [A|B], ... ) :- ... .

as you wanted, and take care to always call it this way: Z = ... , pred( Z = Z , ...). You could even define some meta-predicates to make this translation automatic, like

call_at( Pred, Arg ) :- call(Pred, Arg = Arg).

The = itself here is syntactic, and its use protocol is what gives it its special meaning, i.e. semantics. In Lisp parlance, Prolog's terms are always unevaluated, symbolic.

Will Ness
  • 70,110
  • 9
  • 98
  • 181
  • It should be possible to use term expansion to take a predicate clause like `p(X as Y) :- r(X).` and rewrite it to `p(Y) :- r(Y)`. Not sure if it is a good idea, but I won't know until I try it out I guess. I have had to solve this issue a couple of times; the clean solution has been, contrary to your suggestion, to write `p([A|B]) :- X = [A|B], ...` since usually the pattern matching in the head is useful and necessary, but sometimes it feels silly to keep on writing `[A|B]` in the body when you just need the whole thing. – User9213 Mar 27 '19 at 10:16
  • you meant contrary to the *first* suggestion. right, agreed. so, with the double argument, or `p( X = [A|B] ) :- ...`, there *is* the pattern matching in the head. the Lisp programmer in me is always so wary about the unneeded consing, it's ridiculous... they hammer it into you, "consing is *terrible!*"... so maybe the right answer is your suggestion, and just not using the as-patterns in Prolog at all. OTOH some patterns are more involved than that, there's nested patterns, etc. Indeed copying the same complex piece of code twice is not good practice. – Will Ness Mar 27 '19 at 10:27
  • This is about consing, but the other way. Since `[_|_]` and `[]` are terms of different _shape_ (the atom `[]` and the term with functor `./2`), putting them in the head is what makes your typical `p([]) :- ... . p([H|T]) :- ... , p(T).` deterministic and constant-memory. – User9213 Mar 27 '19 at 11:12
  • This has been tried a lot of times in Prolog. And it always failed. The rule head is just for syntactic unification. Failed attempts to change this were the original version of CLP(R) and many such. Also, adding an extra equation is not that a burden - and it immediately leads to much more general extensions. – false Mar 27 '19 at 14:02
  • @false what's wrong with the duplicated argument approach? and even calling `Z=[1,2,3], pred(Z = Z)`, it is also syntactically unified. – Will Ness Mar 27 '19 at 14:04
  • 1
    I should have been clearer: I was only referring to your first sentence. Any interpretation other than syntactic unification has failed miserably. Also note that in Haskell you have very severe restrictions due to matching (only one occurrence of a variable) &ct – false Mar 27 '19 at 14:11
  • @false ah, yes, the splitting of the argument is per-predicate, not per-clause, precisely because the pattern matching is not *succeed-once-only*. – Will Ness Mar 27 '19 at 14:16
  • Concerning `p(X = s(Y)) :-` there is the PLM (DEC10 Prolog) and the VAM that do not create an auxiliary datastructure. All other machines would have to perform some extra optimizations. Compare this to `p(X) :- X = s(Y), ...` which in the WAM gives optimal code right out of the box (except for clause indexing, indeed). – false Mar 27 '19 at 14:22
2

You can always write:

example([Row|Rows]) :-
  do_something_with_x([Row|Rows]),
  do_something_with_row(Row),
  ...
Paulo Moura
  • 18,373
  • 3
  • 23
  • 33
  • Yes I know. But is it possible to do it in the clause head, or was I remembering my Prolog wrong (it's been a while since I used it)? – user1992634 Mar 27 '19 at 02:45
  • @user1992634 To my untrained eye it looks like it is in the clause head. You should explain the semantics of your example above. Can you try to provide the intended meaning, maybe in pseudocode? – User9213 Mar 27 '19 at 07:37
  • 1
    @User9213, what I'm talking about is the "X=" portion in the clause head of my code. You can compare that with Paulo Moura's code, which doesn't have it. I thought Prolog allowed it. I don't know if I've encountered a bug, if it's just an issue with SWI-Prolog, or... if indeed Prolog doesn't allow it. – user1992634 Mar 27 '19 at 07:41
  • @user1992634 Yes Prolog allows it, but the question is what it means. See my answer. – User9213 Mar 27 '19 at 08:02
0

Here is a silly piece of code that just prints its arguments. It does that to show what the arguments are and to irk the purists:

example(X = [H|T]) :-
    format("X: ~w; H: ~w; T: ~w~n", [X, H, T]).

And now you can use it:

?- example(List = [a,b,c]).
X: _9150; H: a; T: [b,c]
true.

?- example(foo = [bar|baz]).
X: foo; H: bar; T: baz
true.

?- example(X).
X: _8700; H: _8706; T: _8708
X =  (_8700=[_8706|_8708]).

And so on. Play around with it. But still you need to explain what you actually want to achieve with your program.

User9213
  • 1,316
  • 6
  • 12
  • Well, all I'd like to achieve is to save screen space and one less line of code. I don't want to use a line of code to write "X = ..." when I could achieve this in the head. I do this in languages like Erlang. When I try it in SWI-Prolog it gives me problems. Sometimes it kind of works, sometimes it doesn't. If someone else doesn't provide a better answer, I'll accept yours as correct. What Prolog are you using? – user1992634 Mar 27 '19 at 08:35
  • Yes you used it in the clause head, but you are also using it in your query when you run the Program. I don't want to do that, I just want to do it in the clause head. – user1992634 Mar 27 '19 at 08:38
  • @user1992634 Look at the last query. I am not using any `=` there. But again, and I don't know how to formulate this so that you grasp the meaning of my words, and yet I will try again: what concrete problem are you solving? "I don't want to use a line of code" or "I do _this_ (what?) in languages like Erlang" are not programming problems; those are your personal wishes. They do not help me understand what your program is supposed to do. This has nothing to do with `=` or heads or bodies. – User9213 Mar 27 '19 at 08:50
  • 2
    @User9213 the OP wants the ["as-patterns"](https://stackoverflow.com/q/27467650/849891) functionality of Haskell (and turns out, Erlang also has them). – Will Ness Mar 27 '19 at 09:40
  • @WillNess aha! I wish someone would write up a better question. OP, could you please re-word your question? It would make it so much more useful to others struggling with the same problem. – User9213 Mar 27 '19 at 09:53
  • @User9213 done. :) It's a bit verbose, but I always remember the famous saying about the tailor and the cobbler, and not paying the slightest attention to the elegance of the presentation, in favor of its clarity! – Will Ness Mar 27 '19 at 10:04
  • 1
    @WillNess There _is_ elegance in clarity; it cannot be matched by the kind of elegance that only tries to draw a veil over the eye of the mind. – User9213 Mar 27 '19 at 10:09
  • there's also the matter of the intended audience. :) – Will Ness Mar 27 '19 at 10:16
  • Please remark that the last example containing variables not necessarily prints the very same variable name in both lines. This may happen due to garbage collections or otherwise. Also, the standard just guarantees that the same variable name is used within a single write. – false Mar 27 '19 at 14:43
  • @false Thank you for the information. This code was just meant to provoke an answer to my question, "what do you mean" (which WillNess managed to answer!). I sure hope no one is paying attention to the variable names. – User9213 Mar 27 '19 at 15:16