Base types:
---
| none | No term (always fails) | {fail} |
| not(Type) | Not has type Type | {\+ has_type(Type, Term)} |
| var(Type) | Variable or has type Type | var; Type |
| part | Partially instantiated | callable, not(ground) | #####
| attvar | Attributed variable | {attvar(Term)} |
| blob | Blob | {blob(Term, _)} | #####
| blob(BType) | Blob of "type" BType | {blob(Term, BType)} | #####
| engine | Engine handle | {is_engine(Term)} |
| thread | Thread handle | {is_thread(Term)} |
| stream | Stream handle | {is_stream(Term)} |
Common types:
---
| mod(Type) | Module-qualified | atom':'Type | #####
| predind | Predicate indicator | atom'/'nonneg; atom'//'nonneg |
| order | One of [, <, >] | ground, {memberchk(Term, [, <, >])} |
| char | 1-character atom | atom, {atom_length(Term, 1)} |
| chars | List of char | list(char) | **
| code | Unicode character code | int(1, 0x10ffff) |
| codes | List of code | list(code) | **
| formatted | Text or format-args. | text'-'list; text |
| error | Formal error | 'error'(callable, any) |
| fname | File name | text |
| fpipe | File pipe(Cmd) | 'pipe'(atom; string) |
| fspec | File name or pipe(Cmd) | fname; fpipe | #####
| pair | Pair | pair(any, any) |
| pair(KType, VType) | Pair of type KType, VType | KType'-'VType |
Numeric types:
---
| num | Number | {number(Term)} |
| int | Integer | {integer(Term)} |
| float | Float | {float(Term)} |
| ratio | Rational | {rational(Term)} |
| nonneg | Non-negative integer | int, {Term >= 0} |
| nonpos | Non-positive integer | int, {Term =< 0} |
| posint | Positive integer | int, {Term > 0} |
| negint | Negative integer | int, {Term < 0} |
| num(L, H) | Number in [L; H] | num, {L =< Term, Term =< H} |
| int(L, H) | Integer in [L; H] | int, {L =< Term, Term =< H} |
| float(L, H) | Float in [L; H] | float, {L =< Term, Term =< H} |
| ratio(L, H) | Rational in [L; H] | ratio, {L =< Term, Term =< H} |
Type aliases:
---
| partial | Partially instantiated | part |
| module(Type) | Module-qualified | mod(Type) |
| structure | Structure (aka callable) | callable |
| formal_error | Formal error | error |
| number | Number | num | **
| integer | Integer | int | **
| rational | Rational | ratio | **
| positive_integer | Positive integer | posint | **
| negative_integer | Negative integer | negint | **
| between(L, H) | Number in [L; H] | num(L, H) | **
| ratio(L, H) | Rational in [<span styl
?- use_module(library(clpfd)).
true.
?- X #= Y.
X = Y,
Y in inf..sup.
?- X #= Y[1,2].
ERROR: Syntax error: Operator expected
ERROR: X #=
ERROR: ** here **
ERROR: Y[1,2] .
Hi Julio,
> On 17/09/2016, at 11:08, Julio Di Egidio <ju...@diegidio.name> wrote:
>
> Hello Jan, guys,
>
> I'd propose the following types be added to library(error).
>
> In the list, I am including some types that already exist because the
> definition changes slightly, but the functionality stays the same (mainly
> introducing some aliases).
>
> I am also using a classification that I myself do not find satisfactory,
> but if we could sort out a good classification, I think that might help the
> readability of the docs (as the list of types becomes quite longer).
>
> If overall you like the idea, please feel free to propose specific
> amendments/deletions/additions. If we get to an agreement (a final list),
> I'll be glad to do the coding (unless someone else wants to).
I recently added a "type" object to the Logtalk library and, in doing so, I looked into the existing similar support across several Prolog systems. The documentation (includes a list of all types) and the code are available at:
http://logtalk.org/library/type_0.html
https://github.com/LogtalkDotOrg/logtalk3/blob/master/library/type.lgt
Part of designing a type library comes down to design choices (with portability being a main goal in the Logtalk case) but you may find something useful in the links above.
The ISO standard defines a couple of types. For example
the following types I dont find in your list:
byte : 0..255 /* first occurence in glossary section 3.22 */
byte_or_eof : union type of byte and integer -1
/* callled in_byte in the standard, see section 8.13.1.1 */
char_or_eof : union type of char and atom 'end_of_file'
/* callled in_character in the standard, see section 8.12.1.1 */
code_or_eof : union type of code and integer -1
/* callled in_character_code in the standard, see section 8.12.1.5 */
What else?
On a side note, I dont find type checking for:
dicts??
Also it should be checked whether all the extra types of
SWI-Prolog, such as string, blob, dict, etc. satisfy the
clauses of 5.5.4 Types in the iso standard.
For FFIs (foreign function interfaces), it might be also useful to
have types such as int16, uint32, float32 and array types.
In ECLiPSe and untyped array is just a compound with functor
'[]'. But similar to homogenous lists, one might also define homogenous
arrays over some other type. Such an array of byte.
On 19/09/2016, at 16:39, Julio Di Egidio <ju...@diegidio.name> wrote:
<snip>
> I'd have a reservation on a types/1, as I think that already belongs to a "structured" type system (i.e. checking structures, then also disjunctions and conjunctions, as well as partial instantiations, etc.), not necessarily to a low level infrastructure.
Possibly. But the cost of having it is low and is handy for some of the Logtalk built-in predicates (e.g. implements_protocol/2-3 can take either an object or a category identifier as the first argument).
> E.g. if I declare N:between(1,10), I do mean a type, not a type plus a domain restriction.
The domain is quite useful. Take between(Type, Lower, Upper). Assuming that I'm checking number intervals, if we pass e.g. Type = integer or Type = float, we're forbidding mixed type arithmetic. But if we want it, we can simply do e.g. Type = number.
For collection types such as one_of(Type, Set), the Type argument allows us to distinguish some set membership cases that would not be possible without it. Another reason, of course, is that you want the domain errors.
> Conversely, I'd rather propose a check_domain/3, aside an explicit check_type/2, kind of predicate where not only the type but also a domain is passed in as argument, essentially to keeping two notions, of type and domain, distinct.
That's certainly possible. Although I prefer the combination of both domain and type errors being generated by the same predicate, the user can always add the check_domain/3 predicate to the library "type" object (using a complementing category). And, of course, nothing prevents adding e.g. between/2 or one_of/1.
> On 20/09/2016, at 07:53, Julio Di Egidio <ju...@diegidio.name> wrote:
> On Monday, September 19, 2016 at 6:20:25 PM UTC+2, Paulo Moura wrote:
> > On 19/09/2016, at 16:39, Julio Di Egidio <ju...@diegidio.name> wrote:
> <snip>
> > > I'd have a reservation on a types/1, as I think that already belongs to a "structured" type system (i.e. checking structures, then also disjunctions and conjunctions, as well as partial instantiations, etc.), not necessarily to a low level infrastructure.
>
> > Possibly. But the cost of having it is low and is handy for some of the Logtalk built-in predicates (e.g. implements_protocol/2-3 can take either an object or a category identifier as the first argument).
>
> Eventually, I think I agree, and adding the "structured" types is easy enough: I suppose and/2 (possibly as ','/2), or/2 (as ';'/2) and not/1 (as ??) plus a partial/1 (as ??) should be enough, agreed?
Possibly. But the reality is that, no matter which structured types you use, specs can become quite verbose easily. e.g. types([var,object_identifier,category_identifier]) or or(var,or(...,...)) or any other variation. If a spec is common, I suspect most users would simply prefer to define a new type with a nice shorter name than use these structured types heavily.
> > E.g. if I declare N:between(1,10), I do mean a type, not a type plus a domain restriction.
>
> The domain is quite useful. Take between(Type, Lower, Upper). Assuming that I'm checking number intervals, if we pass e.g. Type = integer or Type = float, we're forbidding mixed type arithmetic. But if we want it, we can simply do e.g. Type = number.
>
> For collection types such as one_of(Type, Set), the Type argument allows us to distinguish some set membership cases that would not be possible without it. Another reason, of course, is that you want the domain errors.
>
> Well, no, I really don't: unless you have specific reasons why I should...
In my case, I do want/need the domain (also the instantiation) errors. A main reason is a future type-checker that would signal errors following the predicate specs (which specify instantiation, type, and domain errors).
On Tuesday, September 20, 2016 at 1:09:54 PM UTC+2, Paulo Moura wrote:> On 20/09/2016, at 07:53, Julio Di Egidio <ju...@diegidio.name> wrote:
> > On Monday, September 19, 2016 at 6:20:25 PM UTC+2, Paulo Moura wrote:
> > > On 19/09/2016, at 16:39, Julio Di Egidio <ju...@diegidio.name> wrote:
> > E.g. if I declare N:between(1,10), I do mean a type, not a type plus a domain restriction.
>
> The domain is quite useful. Take between(Type, Lower, Upper). Assuming that I'm checking number intervals, if we pass e.g. Type = integer or Type = float, we're forbidding mixed type arithmetic. But if we want it, we can simply do e.g. Type = number.
>
> For collection types such as one_of(Type, Set), the Type argument allows us to distinguish some set membership cases that would not be possible without it. Another reason, of course, is that you want the domain errors.
>
> Well, no, I really don't: unless you have specific reasons why I should...
In my case, I do want/need the domain (also the instantiation) errors. A main reason is a future type-checker that would signal errors following the predicate specs (which specify instantiation, type, and domain errors).
I don't think that is a good argument: as said, with that approach, we cannot have just the pure type errors, while by keeping the two notions, of type and domain, distinct, everything can be done (including what you want, at the cost of few extra-lines of code and not even for the user) and consistently, while with your way or SWI's consistency is simply lost and unrecoverable... And now that is a serious issue, because it rather makes library(error) fundamentally broken. OTOH, it's true that the same logic is all over the place already, including the C interface as Jan notices above, and while this to me, as said, makes the whole thing fundamentally broken, I am not sure we can reasonably do anything at all about it... other than, indeed, just forgetting about it and roll out our own: and I mean, both library(error) and the native checked interface... Hmm....
> <mailto:swi-prolog+unsub...@googlegroups.com>.
> <mailto:swi-prolog+unsub...@googlegroups.com>.
Personally I think the whole domain/type argument is rather
artificial.
On 09/26/2016 02:56 PM, Julio Di Egidio wrote:
> On Thursday, September 22, 2016 at 9:43:54 AM UTC+2, Jan Wielemaker
> wrote:
>
>> Personally I think the whole domain/type argument is rather
>> artificial.
>
> Eventually, I agree, but also because I was wrong: between/2 does not
> give domain errors, at the moment only cyclic/0 and acyclic/0 do, and
> that is the only thing I'd fix, that is, I'd make these two also
> throw type errors: then, IMO, it'd be all clean and clear! -- Can
> we do it?
Well, I consider this a bug:
6 ?- must_be(between(1, 10), 13).
ERROR: Type error: `between(1,10)' expected, found `13' (an integer)
ERROR: In:
ERROR: [10] throw(error(...,_414))
If there is a case for a domain error, this should be the one. I
wouldn't be against dropping domain_error altogether if we had to start
from scratch, but they exist in the standard.
I really have no clue whether violating (a)cyclic constraints is a type
or a domain error ... Maybe someone has a well motivated argument ...
On 09/26/2016 02:56 PM, Julio Di Egidio wrote:
> I'd also propose that for now we only focus on adding missing type
> definitions in library(error) with the only and specific goal of
> *completing coverage of the argument types that appear in built-in
> predicates* (with few exceptions maybe, e.g. I'd like an interval
> numeric type). -- I'll list these in a separate post.
Agree.
Base types:
---
| any | Any term (always true) | {[]>>true} |
| none | No term (always fail) | {[]>>fail} | * ###
| var | Variable (may be attributed) | {var} |
| nonvar | Not a variable | {nonvar} |
| ground | Ground | {ground} |
| atom | Atom | {atom} |
| atomic | Atomic | {atomic} |
| compound | Compound | {compound} |
| callable | Callable | {callable} |
| cyclic | Cyclic | {cyclic_term} |
| acyclic | Acyclic | {acyclic_term} |
| partial | Partial callable | callable, not(ground) | * ###
| not(T) | Not has type T | {\+ has_type(T)} | * ###
| var(T) | Variable or has type T | var; T | * ###
System types:
---
| blob | Blob | {[]>>blob(Term, _)} | * ###
| blob(BT) | Blob of "type" BT | {[BT]>>blob(Term, BT)} | * ###
| attvar | Attributed variable | {attvar} | *
| varnonatt | Variable not attributed | var, not(attvar) | * ###
| string | String | {string} |
| dict | Dictionary | {is_dict} |
| engine | Engine handle | {is_engine} | *
| thread | Thread handle | {is_thread} | *
| stream | Stream handle | {is_stream} |
| encoding | A supported encodings (*) | {current_encoding} |
(*) Currently supported encodings are ...
Common types:
---
| mod(T) | Optionally module-qualified | s(atom:T); T | * ###
| predind | Predicate indicator | s(atom/nonneg); s(atom//nonneg) | *
| boolean | One of true, false | oneof([true, false]) | *
| boolish | One of true, false, yes, no | oneof([true, false, yes, no]) | * ###
| order | One of , <, > | oneof([, <, >]) | *
| error | Formal error | s(error(callable, any)) | *
| fname | File name | text | *
| fpipe | File pipe(Cmd) | s(pipe(atom; string)) | *
| fspec | File name or pipe(Cmd) | fname; fpipe | * ###
| pair | Pair | pair(any, any) | *
| pair(TK, TV) | Pair of type TK, TV | s(TK-TV) | *
| oneof(L) | Ground element of list L | ground, {[L]>>memberchk(Term, L)} |
Text types:
---
| char | 1-character atom | atom, {[]>>atom_length(Term, 1)} | *
| chars | List of char | list(char) |
| code | Unicode character code | int(1, 0x10ffff) | *
| codes | List of code | list(code) |
| text | Text | atom; string; chars; codes |
| formatted | Text or format-args. | s(text-list); text | *
List types:
---
| list | List | {is_list} |
| open_list | Open (aka partial) list | {is_open_list} | * ###
| diff_list | Difference list | {is_diff_list} | * ###
| list(T) | List of type T | {is_list_type(T)} |
| open_list(T) | Open list of type T | {is_open_list_type(T)} | * ###
| diff_list(T) | Difference list of type T | {is_diff_list_type(T)} | * ###
Numeric types:
---
| num | Number | {number} | * ###
| int | Integer | {integer} | * ###
| float | Float | {float} |
| ratio | Rational | {rational} | * ###
| ivl_btype | Interval bound "type" | oneof([closed, open, c, o]) | * ###
| ivl_bound(NT) | Interval bound of type NT | oneof([inf, -inf, +inf]); NT | * ###
| ivl_gt(NT, L, LT) | Number of type NT in ?L..+inf] | (*1) | * ###
| ivl_lt(NT, H, HT) | Number of type NT in [-inf..H? | (*2) | * ###
| ivl(NT, L, H, LT, HT) | Number of type NT in ?L..H? | ivl_gt(NT, L, LT), ivl_lt(NT, H, HT) | * ###
| between(NT, L, H) | Number of type NT in [L; H] | ivl(NT, L, H, c, c) | * ###
| between(L, H) | Number in [L; H] | (*3) | * ###
| arith(NT, V) | Evaluated is V of type NT (*) | (*4) | * ###
| arith(NT) | Evaluated has type NT (*) | arith(NT, _) | * ###
(*) Evaluates a copy of Term with is/2. Unifies the result with V in arith/2.
(*1) ivl_gt(NT, L, LT) --->
NT, {[L, LT]>>
(( memberchk(LT, [closed, c]) % Term in [L..+inf]
-> ( memberchk(L, [-inf]) % if Term in [-inf..+inf]
-> true % -> true
; memberchk(L, [inf, +inf]) % else if Term in [+inf..+inf]
-> memberchk(Term, [inf, +inf]) % -> Term == inf (or +inf)
; Term >= L % else Term >= L
)
; memberchk(LT, [open, o]) % Term in (L..+inf]
-> ( memberchk(L, [-inf]) % if Term in (-inf..+inf]
-> \+ memberchk(Term, [-inf]) % -> Term =\= -inf
; \+ memberchk(L, [inf, +inf]) % else if Term in (L..+inf]
-> ( memberchk(Term, [inf, +inf]) % -> if Term == inf (or +inf)
-> true % -> true
; Term > L % else Term > L
)
)
))}
(*2) ivl_lt(NT, H, HT) --->
... symmetric to (*1) ...
(*3) between(L, H) --->
{[L, H]>>
(( has_type(int, L),
has_type(int, H)
))} -> between(int, L, H); between(num, L, H)
(*4) arith(NT, V) --->
{[NT, V]>>
(( catch(V is Term, _, fail),
has_type(NT, V)
))}
Type aliases:
---
| module(T) | Optionally module-qualified | mod(T) | * ###
| modcall | Opt. mod-qualif. callable | mod(callable) | * ###
| predicate_indicator | Predicate indicator | predind | * ###
| structure | Structure (aka callable) | callable | * ###
| formal_error | Formal error | error | * ###
| list_or_partial_list | List or open list | list; open_list | * ###
| number | Number | num | * ###
| integer | Integer | int | * ###
| rational | Rational | ratio | * ###
| positive_integer | Positive integer | posint | * ###
| negative_integer | Negative integer | negint | * ###
| nonneg | Non-negative integer | ivl_gt(int, 0, c) | * ###
| nonpos | Non-positive integer | ivl_lt(int, 0, c) | * ###
| posint | Positive integer | ivl_gt(int, 0, o) | * ###
| negint | Negative integer | ivl_lt(int, 0, o) | * ###