3

call(Goal,Arg) allows to append the argument Arg to the arguments of Goal and call the resulting goal, e.g.

call(succ(1), R).

is the same as

succ(1, R).

However, I don't want to append to the argument list, but instead prepend, e.g.

callpre(succ(1), R).

should result in

succ(R, 1).

How can I prepend arguments to the list of Goal's arguments and call the resulting goal?

false
  • 10,264
  • 13
  • 101
  • 209
morxa
  • 3,221
  • 3
  • 27
  • 43
  • 2
    [Similar question for `maplist`](http://stackoverflow.com/questions/36582088/is-there-a-better-method-than-making-a-new-rule-to-change-variable-order-for-map). I would prefer an answer that is not "use that library" though, which is what we get for that question. – Fatalize Oct 21 '16 at 14:12
  • 2
    Your description of `call/2` in the first sentence of your question is not correct. `call/2` does not takes a list of arguments to `Goal` in its second argument. It take a single additional argument to `Goal`, as you then describe correctly. – Eyal Oct 21 '16 at 18:31
  • @Eyal thank you, fixed it. – morxa Oct 21 '16 at 18:37
  • @Fatalize The question you linked already shows how to do it _without_ a library. –  Oct 22 '16 at 07:19

2 Answers2

2

For an arbitrary number of arguments, you could define it as

callpre(MGoal, Arg) :-
    strip_module(MGoal, M, Goal), 
    Goal =.. [F | Args],
    NewGoal =.. [F, Arg|Args], 
    M:NewGoal.

You'll also need a meta_predicate/1 declaration for this:

:- meta_predicate callpre(1, *). 
Eyal
  • 1,094
  • 10
  • 16
  • 1
    Your meta_predicate declaration is well meant but inaccurate. It rather should be `:- meta_predicate(:, *)`. A `1` alone means that one argument is missing **and** that one has to call it with `call(G_1, A_1).` It is this second part that you are violating. – false Oct 22 '16 at 16:43
  • @Boris: can you elaborate? – false Oct 22 '16 at 16:43
  • 1
    Also you need to handle modules. With your declaration, `(:)/3` is called. – false Oct 22 '16 at 17:59
  • @false I don't understand what you mean by using `:` instead of `1` in the `meta_predicate` declaration. The SWI Prolog documentation says that `:` means `The argument is module-sensitive, but does not directly refer to a predicate. For example: consult(:)`. As for the problem with `(:)/3', thanks, I've fixed the answer. – Eyal Oct 22 '16 at 18:13
  • 1
    `1` means: the argument must be called with one additional argument with `call/2`. But you are adding the additional argument in the beginning. Therefore cross-referencing will not work accurately. – false Oct 22 '16 at 18:24
  • I see. I thought the cross-referencer is just concerned with the arity of the predicate, not the order of the arguments. What's an example of something that would not work accurately? – Eyal Oct 22 '16 at 19:06
  • @Eyal: It makes a significant difference, if the predicate itself is again a meta-predicate: Then, the arguments get shifted/rotated by one for the analysis. So some undefined predicates are missed, and others are incorrectly considered predicates etc. – false Oct 22 '16 at 20:50
1

You could define that as:

callpre(Goal,Z):-
  Goal=.. [Predicate, Y],
  Goal2=.. [Predicate, Z],
  call(Goal2,Y).

if Goal has more than one arguments you could write:

  callpre(Goal,Z):-
     Goal=.. [Predicate,Arg1| Y],
     reverse([Arg1|Y],[H|T]),
     reverse(T,T1),
     Goal2=.. [Predicate,Z| T1],
     call(Goal2,H).

(this also works for one argument)

Note that =../2 operator unifies a functor and arguments with a list e.g succ(X,Y)=.. [succ,X,Y].

coder
  • 12,832
  • 5
  • 39
  • 53