Composing an arithmetic function in a loop (but without univ)

The title is not very descriptive, I know. I was trying to code-golf the shortest Prolog program I could think of to sum a a list of integers. I came up with this:

solve([],S,S).
solve([N|Ns],S0,Acc):-
        solve(Ns,N+S0,Acc).

Which can then be called as follows, delegating the execution of the addition operations to the top-level:

45 ?- _Ls = [1,2,3], user:solve(_Ls,0,S), S_ is S.
S = 3+(2+(1+0)),
S_ = 6.

So far, so Prolog-y and I thought that’s a nice trick because it exposes Prolog’s internal workings, i.e how it does not “derference” functions and how everything is a term etc.

Then I thought why not generalise this so that the operator is passed to the solve predicate so that it can then do arbitrary operations on integers? Something like apply-list etc. So I tried this:

solve([],_,S,S).
solve([N|Ns],Op,S0,Acc):-
        solve(Ns,(N,Op,S0),Acc).

And immediately I was stumped by the behaviour of the runtime which seems to treat the first interpretation of the operator, stored in the argument Op differently than every subsequent interpretation of the same operator:

47 ?- _Ls = [1,2,3], _Op = +, user:solve(_Ls,_Op,0,S).
S = 3+(2+(1, (+), 0)).

Here’s the trace of the top-level query:

[debug] 49 ?- _Ls = [1,2,3], _Op = +, user:solve(_Ls,_Op,0,S).
   Call: (15) solve([1, 2, 3], +, 0, _23890) ? leap
   Call: (16) solve([2, 3], (1, (+), 0), _23890) ? leap
   Call: (17) solve([3], 2+(1, (+), 0), _23890) ? leap
   Call: (18) solve([], 3+(2+(1, (+), 0)), _23890) ? leap
   Exit: (18) solve([], 3+(2+(1, (+), 0)), 3+(2+(1, (+), 0))) ? leap
   Exit: (17) solve([3], 2+(1, (+), 0), 3+(2+(1, (+), 0))) ? leap
   Exit: (16) solve([2, 3], (1, (+), 0), 3+(2+(1, (+), 0))) ? leap
   Exit: (15) solve([1, 2, 3], +, 0, 3+(2+(1, (+), 0))) ? leap
S = 3+(2+(1, (+), 0)).

Note that the end result here is not a valid arithmetic function, so this raises an error:

52 ?- _Ls = [1,2,3], _Op = -, user:solve(_Ls,_Op,0,S), S_ is S.
ERROR: Arithmetic: `(-)/0' is not a function
ERROR: In:
ERROR:   [15] _52778 is 3+(2+ ...)
ERROR:   [13] toplevel_call(user:user: ...) at c:/program files/swipl/boot/toplevel.pl:1529
ERROR: 
ERROR: Note: some frames are missing due to last-call optimization.
ERROR: Re-run your program in debug mode (:- debug.) to get more detail.

So what’s going on here? Why is the result of the first (N, Op, 0) different than any subsequent one, and how is it different? Is this an expansion hook? I never use those so I’m not even sure where to look for it.

Btw just to be clear: the point is not the code golf anymore, nor even to compose an arithmetic function which I reckon I could do with univ without trouble, I’m just trying to figure out what’s going on with this weird syntactic quirk of Prolog that I hadn’t noticed before. Happens every few years :slight_smile:

Your solve/4 predicate’s recursive call is to solve/3. If your first solve/3 implementation is visible it explains the behaviour.

solve(,_,S,S).
solve([N|Ns],Op,S0,Acc):-
Expr =.. [Op,N,S0],
solve(Ns,Op,Expr,Acc).

?- solve([1,2,3],+,0,S).
S = 3+(2+(1+0)).
?- solve([1,2,3],,1,S).
S = 3
(2*(1*1)).

You want this? :slight_smile:

Sorry, I had missed the ‘without univ’ in the title.
Will functor/3 and arg/3 do it?

Seems you’re fighting op/3 syntax…

103 ?- X is +(1,+(2,3)).
X = 6.


then

solve([N],N).
solve([H|T],+(H,R)) :- solve(T,R).

yields

107 ?- solve([1,2,3],E),X is E.
E = 1+(2+3),
X = 6 .

HTH