]> Repositorios git - scryer-prolog.git/commitdiff
ADDED: can_be(term, ...) and must_be(term, ...)
authorMarkus Triska <[email protected]>
Wed, 20 Apr 2022 18:41:11 +0000 (20:41 +0200)
committerMarkus Triska <[email protected]>
Wed, 20 Apr 2022 20:15:32 +0000 (22:15 +0200)
This terminology is a bit harder to understand than that of the other
type checks.

Scryer Prolog comprises two individually consistent but incompatible
notions of terms, namely terms and rational trees (which are also
called "cyclic terms"). This leads to a confusion in terminology,
because cyclic terms are not terms in the former sense even though
they also contain the word "term" in their name.

This terminological confusion is not restricted to terms: We also find
it for example in the notion of lists. Lists are defined inductively:

    - The empty list (written as "" or [], called 'nil') is a list.
    - A term with principal functor '.'/2 is a list if its second argument
      is a list.

Note that a list may contain cyclic terms as elements, and still be
called a list. However, if we post:

    ?- Ls = [_|Ls].

then Ls is not a list, because it does not fit the inductive
definition. Still, it is sometimes referred to as a "cyclic list" or
"circular list", even though it is not a list. Even Prolog
implementors that aim for strict conformance to the Prolog ISO
standard and are deeply familiar with the standard use this
terminology when talking about such terms, see for example:

    https://github.com/ichiban/prolog/pull/198

Consequently, must_be(list, Ls) raises a *type error* in this case.

Terms are distinguished from cyclic terms completely analogously. This
leads to the counterintuitive consequence that *cyclic lists are not
terms*. The reason for this apparent contradiction in terminology is
the mutual incompatibility of different notions of terms. The notion
of "term" precludes rational trees, similar to the notion of "list"
which precludes cyclic lists.

We therefore introduce:

    * must_be(term, Term)
      - raises an instantiation error if Term contains variables (since
        it could still become a rational tree)
      - raises a type error if Term is a rational tree

    * can_be(term, Term)
      - raises a type error if Term is a rational tree

Note that using the notion of "cyclic term" and "acyclic term" in this
terminology would not be a good idea, because this confusion is
precisely what we want to eliminate: must_be(term, Term) should be
usable as a test that reliably determines whether Term can be treated
as a term with the logical properties we expect from terms.

This addresses #1428.

src/lib/error.pl

index c5af6d7c4290c74c508ca138bf414cee7d8d17f5..68d7d3ee71c6be2f77475b02dc02938a7fed73fa 100644 (file)
@@ -30,6 +30,7 @@
        - chars
        - integer
        - list
+       - term
 - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - */
 
 must_be(Type, Term) :-
@@ -58,6 +59,13 @@ must_be_(chars, Ls) :-
 must_be_(list, Term)    :- check_(error:ilist, list, Term).
 must_be_(type, Term)    :- check_(error:type, type, Term).
 must_be_(boolean, Term) :- check_(error:boolean, boolean, Term).
+must_be_(term, Term)    :-
+        (   \+ ground(Term) ->
+            instantiation_error(must_be/2)
+        ;   \+ acyclic_term(Term) ->
+            type_error(term, Term, must_be/2)
+        ;   true
+        ).
 
 % We cannot use maplist(must_be(character), Cs), because library(lists)
 % uses library(error), so importing it would create a cyclic dependency.
@@ -94,6 +102,7 @@ type(chars).
 type(list).
 type(var).
 type(boolean).
+type(term).
 
 /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
    can_be(Type, Term)
@@ -122,6 +131,11 @@ can_(character, T)  :- character(T).
 can_(chars, Ls)     :- '$is_partial_string'(Ls).
 can_(list, Term)    :- list_or_partial_list(Term).
 can_(boolean, Term) :- boolean(Term).
+can_(term, Term)    :-
+        (   acyclic_term(Term) ->
+            true
+        ;   type_error(term, Term, can_be/2)
+        ).
 
 list_or_partial_list(Ls) :-
         '$skip_max_list'(_, _, Ls, Rs),