First argument indexing

YAP is another Prolog system providing extending indexing of predicate clauses:

$ yap
YAP 6.3.4 (x86_64-darwin14.3.0): Wed Apr 22 22:26:34 WEST 2015
 ?- [user].
 % consulting user_input...
foo(h(X),X).
|     foo([],nil).
|     foo([_|_],cons).
|     foo(X,Y) :- integer(X), Y = n(X).
|      % consulted user_input in module user, 1 msec 0 bytes
true.
 ?- foo([],_).
true.

Some relevant papers on YAP indexing features are:

  • Demand-Driven Indexing of Prolog Clauses
  • On Just in Time Indexing of Dynamic Predicates in Prolog

Yes, the ECLiPSe system does this.

As you suggest, it takes into account a number of simple built-in predicates (such as integer/1, =/2, !/0) for indexing purposes. Your example then executes deterministically, without choicepoints, for all calls of foo/2 with the first argument instantiated. Moreover, ECLiPSe would do this on any argument, not just the first.

You can find a little more detail in the paper ECLiPSe - from LP to CLP.

To answer your followup question: No extra VM features are necessary, the generated VM code looks like this:

foo / 2:
    switch_on_type           a(1) 
            list:           ref(L5)
            structure:      ref(L1)
            bignum:         ref(L7)
            []:             ref(L4)
            integer:        ref(L7)
            meta:           ref(L0)
label(L0):
    try                      0     2     ref(L1) 
    retry                    0     ref(L3) 
    trust                    0     ref(L5) 
label(L1):
    get_structure            a(1)     h / 1     ref(L2) 
    write_value              a(2) 
    ret                  
label(L2):
    read_value               a(2) 
    ret                  
label(L3):
    get_nil                  a(1) 
label(L4):
    get_atom                 a(2)     nil 
    ret                  
label(L5):
    get_list                 a(1)     ref(L6) 
    write_void               2 
label(L6):
    get_atom                 a(2)     cons 
    ret                  
label(L7):
    get_structure            a(2)     n / 1     ref(L8) 
    write_value              a(1) 
    ret                  
label(L8):
    read_value               a(1) 
    ret