[m-dev.] pragmas should specify pred/func as well as name and arity

Peter Wang novalazy at gmail.com
Thu Apr 29 14:33:34 AEST 2021


On Thu, 29 Apr 2021 12:38:21 +1000 "Zoltan Somogyi" <zoltan.somogyi at runbox.com> wrote:
> Most pragmas tell the compiler something about a predicate
> or function. We have two pieces of syntax that pragmas use to specify
> *which* predicate or function. The first syntax we implemented was simple name/arity.
> The other, implemented later, is either pred(argmode1, ..., argmodeN),
> or func(argmode1, ..., argmodeN) = retmode.
> The latter of course was intended to be more precise, because it was
> intended to specify one *procedure* of a predicate or function. But it is
> also more precise in another way: it actually specifies whether it applies
> to a predicate or function, which the first syntax does not.
> 
> This leaves open the door to a potential problem where the programmer
> intends a pragma to apply to e.g. pred foo/2, but the compiler also
> applies it to func foo/2.
> 
> This has not been a significant problem in the past, for two reasons.
> First, the first pragmas we implemented (apart from things like foreign_procs,
> which look like pragmas syntactically but which we now handle as the
> totally separate kind of thing they are) were things like pragma inline,
> for which it did not matter all that much whether it applied to both
> a pred and a func, or to just the one the programmer meant.
> The second reason, unique to us, is that we tended to frown on
> having both a pred foo/2 *and* a foo/1, so the issue was rarely relevant,
> though we *do* have some instances of defining e.g. both pred foo/2
> and pred foo/3, and then *function versions of both*, i.e. func foo/1
> and func foo/2.
>  
> However, for pragmas that assert something, such as promise_pure,
> applying them to both a predicate and a function when the programmer
> intended applying them to only one is a more significant problem.
> I believe we should update the pragmas that use the first syntax
> to first allow, and eventually require, the programmer to say whether
> they mean pred foo/2 or func foo/2.

Can we require something more specific than name/arity if there is
actual ambiguity? I already dislike having to specify the arity when
there is only one such thing by that name.

> I can see three broad approaches to how this could be done.
> 
> Approach 1 would be to wrap the name/arity pair in either pred() or func().
> This would look good, but unfortunately the second syntax also uses terms
> whose top function symbol is either pred or func, and there are some pragmas
> that allow the subject that they apply to to be specified using either the first
> or the second syntax (see attached table), so this would probably serve
> as a source of confusion. And the compiler's error messages, having to
> describe two possible ways to fix any syntax errors (one to reach each kind
> of valid syntax), couldn't help as much as we would like them to.

This seems ideal to me. I don't really understand the problem with error
messages. If the compiler cannot parse the term, I think the compiler
can just say something like:

    The argument must have one of these forms:

        NAME/ARITY
        pred(NAME/ARITY)
        func(NAME/ARITY)
        NAME(MODES)

and the user should get the idea.

> 
> Approach 2 would be to add a "_pred" or "_func" suffix to the pragma name,
> such as ":- pragma inline_func(foo/2).". We have already used this approach
> when replacing ":- external(foo/2)." with ":- external_{pred,func}(foo/2).".
> However, there are some pragmas, such as "terminates", for which neither
> that suffix, nor a similar prefix, would look all that natural.

I don't especially like it.

> Approach 3 would be to add an extra  argument, containing simply either
> "pred" or "func", to the pragma's argument list, just before the argument
> that now contains just foo/2.

If the argument was optional then I could ignore it :)

> 
> There is also what we might call "approach 0": simply obsolete the pragmas,
> such as promise_pure, whose effect can be accomplished by wrappers
> around goals.

pragma promise_pure is not redundant because this is correct:

    :- pred foo(io::di, io::uo) is det.
    :- pragma promise_pure(foo/2).

    foo(!IO) :-
        impure do_foo.

but this is wrong:

    foo(!IO) :-
        promise_pure (
            impure do_foo
        ).

I supposed you could ensure that !IO appears inside the impure goal.

Peter


More information about the developers mailing list