I agree that partial evaluation is a key, about which I know only partially though.

BTW, the following `term_expansion`

rule for `maplist_rec/3`

, a recursive version of maplist/3,

displays this for `maplist_rec(plus(1), [0,1,2], Out))`

:

```
% ?- show(maplist_rec(plus(1), [0,1,2], Out)).
%@ pac:pac#12([0,1,2],_), where
%@ pac:pac#12([],[]):-true
%@ pac:pac#12([A|B],[C|D]):-(pac:is_list(A)->pac:pac#12(A,C);pac:plus(1,A,C)),pac:pac#12(B,D)
%@ Out = _.
```

It seems that my pac library already does something like âpartial evaluationâ partially. Is it right ?

Here is a simple statistic for `maplist_rec(plus(1), Ks, Out))`

, where Ks is a list of length 1000, and each element of Ks is a list of length 1000.

```
% ?- N = 1000, K=1000, numlist(1, N, Ns), length(Ks, K),
% maplist(=(Ns), Ks),
% time(maplist_rec(plus(1), Ks, Out)).
%@ % 4,003,002 inferences, 0.136 CPU in 0.146 seconds (93% CPU, 29343004 Lips)
%@ N = K, K = 1000,
%@ Ns = [1, 2, 3, 4, 5, 6, 7, 8, 9|...],
%@ Ks = [[1, 2, 3, 4, 5, 6, 7, 8|...], [1, 2, 3, 4, 5, 6, 7|...], [1, 2, 3, 4, 5, 6|...], [1, 2, 3, 4, 5|...], [1, 2, 3, 4|...], [1, 2, 3|...], [1, 2|...], [1|...], [...|...]|...],
%@ Out = [[2, 3, 4, 5, 6, 7, 8, 9|...], [2, 3, 4, 5, 6, 7, 8|...], [2, 3, 4, 5, 6, 7|...], [2, 3, 4, 5, 6|...], [2, 3, 4, 5|...], [2, 3, 4|...], [2, 3|...], [2|...], [...|...]|...].
```

```
etc(maplist_rec, [F|As], _, meta:G, P, P):- var(F), !,
complete_args(maplist_rec, [F|As], G).
etc(maplist_rec, [F|As], M, G, P, Q):-
expand_arg(F, M, F0, P, P0),
term_variables(F0, Vs),
expand_core(
mrec(Vs, [ Main = pred(
[[], []]
& ([[X|Xs], [Y|Ys]]:-
(( is_list(X)
-> call(Main, X, Y)
; call(F0, X, Y)
),
call(Main, Xs, Ys)))
)
]),
M, G0, P0, Q),
complete_args(G0, As, G).
```

EDIT 2021/1/3

```
is_list/1 is expensive !
% ?- N is 10^7, length(X, N), time(repeat(1000, is_list(X))).
%@ % 2,001 inferences, 25.174 CPU in 25.178 seconds (100% CPU, 79 Lips)
%@ N = 10000000,
%@ X = [_2438, _2444, _2450, _2456, _2462, _2468, _2474, _2480, _2486|...].
```

EDIT 2021/1/4

It seems that is_list/1 is not prepared as a fast type checker like integer/1 and atom/1. So it may not be good to use is_list/1 as follows, provided that X has chance to be bound to a long list or cyclic term.

f(X):- integer(X),!, âŚ

f(X):- atom((X),!, âŚ

f(X):- is_list(X), !, âŚ

âŚ

help(is_list) says:

is_list(+Term)

True if Term is bound to the empty list ([]) or a compound term with nameâ

[|]â and arity 2 and the second argument is a list. This predicate acts as

if defined by the definition below on acyclic terms. The implementation

safely fails if Term represents a cyclic list.

```
is_list(X) :-
var(X), !,
fail.
is_list([]).
is_list([_|T]) :-
is_list(T).
```

EDIT 2021/1/4

`is_list(X)`

compared with `(X=[_|_]; X =[])`

. So, I should not use `is_list/1`

for merely checking `functor(X, '[|]', 2)`

.

```
% ?- N is 10^7, length(X, N), time(repeat(1000, (X=[_|_]; X =[]))).
%@ % 1,000 inferences, 0.166 CPU in 0.359 seconds (46% CPU, 6014 Lips)
%@ N = 10000000,
%@ X = [_238, _2888, _2894, _2900, _2906, _2912, _2918, _2924, _2930|...] .
```