Re: Ann: SWI-Prolog 6.5.3
Jan Wielemaker <[email protected]>
| Newsgroups | gmane.comp.ai.prolog.swi |
|---|---|
| Message-ID | <[email protected]> |
Hi Richard,
On 11/20/2013 12:07 AM, Richard A. O'Keefe wrote:
> Jan wrote:
>>
>> - memberchk/2 raises a type error if it encounters a non-list
>> cell.
>> * MODIFIED: memberchk/2 reports a type error if it stumbles on a
>> non-list. Note that it does *not* agressively check the list and thus
>> memberchk(a, [a|b]) succeeds. Notably intended to capture memberchk(C,
>> "String").
>
> The historic definition of memberchk/2 is equivalent to
>
> memberchk(E, [X|Xs]) :- ( X = E -> true ; memberchk(E, Xs) ).
>
> If presented with a predicate where
>
> memberchk(1, [1|2]) succeeds
> memberchk(2, [1|2]) reports a type error
>
> I should be inclined to regard it as buggy and complain bitterly
> while rewriting old code to use a new predicate whose semantics
> I could be sure of.
I'm surely not proud of this. On the other hand runtime (type) checks
have never guaranteed type correctness. I recall the Ulrich claims that
runtime type errors are basically logical failures. Adding runtime type
checks to the predicates merely helps you finding likely bugs in your
programs. Subsequently, if this error is not a logical error, you can
make your program clearer my putting an explicit type test before the
predicate that would raise the exception. Nothing new here.
Now, from a typing perspective, I think we should consider any list that
does not `terminate' in [] or a (constraint) variable an ill-typed data
structure. Ideally, we'd have infrastructure that avoids that these are
created in the first place. It is a bit hard to see an efficient runtime
solution for that. Probably only static type analysis can give you that.
If we consider getting rid of [a|b] a separate problem, memberchk/2
now basically simply checks that the second argument is a list. This
raises some exceptions on existing code. Most of the cases I studied
should be considered highly dubious code. I found only one case where
one could reasonably state that silent failure was anticipated.
> Since there is no 'string' data type in ISO Prolog, it might have
> been more backwards compatible and arguably more useful to make
> memberchk(C, "String") *work*:
>
> memberchk(E, [X|Xs]) :- !,
> ( X = E -> true ; memberchk(X, Xs) ).
> memberchk(C, S) :-
> is_string(S),
> string_codes(S, Cs),
> memberchk(C, Cs).
>
> except that the second clause can of course be implemented without
> building any intermediate data structures.
>
> Personally, I've preferred to avoid using memberchk/2 with the
> second argument a "..." literal. (I haven't _religiously_ done
> so; I've _preferred_ to do so.)
I agree here. The only way to make strings cooperate with list
predicates is to make strings appear as virtual lists on which you can
apply normal (list) unification. You claimed that NU-Prolog does this
and that it is far from simple (if I recall correctly). Even if
possible, I still do not like that, as it will imply that strings may
silently be transformed into code lists. Overloading all list predicates
is (IMHO) a similarly bad idea.
> I suggest that this might be something that would be better handled
> by a lint checker than by a run time check. Even something as crude
> as
> grep 'memberchk.*"' *.pl
> tells me *now* that I don't have any memberchk/2 calls with a
> "string literal" argument without having to wait until some test
> case stumbles into one.
>
> It doesn't make a lot of sense to me to make this change to memberchk/2
> but not to member/2. So
>
> egrep '[^a-z_]member(chk)?[(],*"' *.pl
>
> This could be a new style check. Oh, and of course append/3 has
> _exactly_ the same problem:
>
> memberchk(X, Ys) :- member(X, Ys), !.
> member(X, Ys) :- append(_, [X|_], Ys).
>
> Now this is one where I _haven't_ avoided string literals as arguments,
> and indeed there are quite a few. If my code should be loaded with
> double quotes interpreted as string literals, I'd be in real trouble.
>
> The Prolog version of a "lint check" would be a new style check.
> There is an existing style_check 'string' which is rather
> unfortunate, because it DOES NOT SPECIFY A STYLE CHECK.
> Rather, it specifies a SYNTAX CHANGE.
Thanks for noting. I'll delete it. It is deprecated backward
compatibility for a very long time and it now just causes confusion.
> What we *want* is the style check.
> :- style_check(+double_quote).
> should warn any time a double-quoted literal is passed to a
> "well known" list-processing predicate.
There is something like that in the V7 branch. It is part of
library(check), called list_strings/0. It analyses the loaded program,
looking for string literals that are passed to arguments of predicates
that are not known to be safe. For example, calling format("Hello
world!~n") is supposed to be fine, but append("hello", Rest, List) is
not. It allows declaring additional predicates as `safe'.
>> * FIXED: Always apply canonisePath() after creating an absolute path.
>
> I believe I've mentioned before that to canonise someone or sometime
> is to give him, her, or it official sanction, such as declaring
> someone to be a saint or adopting SI units. The operation of putting
> something into canonical form is canonICALise.
>
> m% man -k canonicalize
> cleanup(8) - canonicalize and enqueue Postfix message
> realpath(3) - returns the canonicalized absolute pathname
Luckily it is a quite unique word. Ran a big sed -i ... on all the
sources :-)
Cheers --- Jan