[m-users.] Free and Ground in the same error line?

Zoltan Somogyi zoltan.somogyi at runbox.com
Tue Oct 31 23:41:20 AEDT 2023


On 2023-10-31 23:13 +11:00 AEDT, "Julien Fischer" <jfischer at opturion.com> wrote:
> 
> On Sat, 28 Oct 2023, Sean Charles (emacstheviking) wrote:
> 
>> I rewrote it like this, could it be any leaner? Asking in case I am not as good as I can be yet :D !
>>     384 get_class_superclass(Class, Super, !X) :-
>>     385     ( if !.X = [ tk(Cp, Cb) | Rest ] then
>>     386         Class = ps(Cp, Cb),
>>     387         Super = no,
>>     388         !:X   = Rest
>>     389     else if !.X = [ sexp(_, [ tk(Cp, Cb), tk(Sp, Sb) ]) | Rest ] then
>>     390         Class = ps(Cp, Cb),
>>     391         Super = yes(ps(Sp, Sb)),
>>     392         !:X   = Rest
>>     393     else
>>     394         fail
>>     395     ).

Note that you *can* write the condition of that first if-then-else as

  if !.X = [tk(Cp, Cb | !:X] then ...

I expect that you wrote Rest in place of !:X there because you cannot bind
variables visible from outside the if-then-else in the condition.
State variable syntax is not an exception to that, but it looks like an exception.
The reason is that the compiler, of course, knows this rule. Therefore if
the condition updates a state variable, then the compiler will itself
add the unification that corresponds to !:X = Rest in your code.

In concrete terms, if the version of !X just before the if-then-else is
version 0 (the initial version, as in this case), and the condition updates it
to the next version, version 1, then the compiler knows that version 1 cannot
be made visible outside the if-then-else. So if the then-part does not itself
update !X to a version later than 1, then the compiler will do so itself,
by adding a unification between versions 1 and N in the then-part,
and will make version N the version visible in the code after the if-then-else.
N will be the maximum of 2 and whatever the current version at the end of
the else part is.

It is therefore possible, and meaningful, to write code such as

 ( if !.X = ["abc" | :!X] then
   true
 else
    true
  )

If !.X is a list of strings, then this will remove an initial "abc"
from that list, if the list starts with that string. The then-part
does not have to do anything. The work is done by the condition,
and the then-part just passes its result to the code after the if-then-else.

>> > I mean, it's fine but in the manual, 2.13 DCG-rules it says "As a
>> matter of style, we recommend that in future DCG notation be reserved
>> for writing parsers and sequence generators, and that state variable
>> syntax be used for passing state threads."
> 
> I would not read too much into that recommendation.  It was written as
> part of the change that added state variables to the Mercury language.
> At that point, no one had much experience using, for parsers or sequence
> generators, or anthing else.  My opinion, with the benefit of twenty
> years of hindsight, is don't use DCGs.  (The one exception to that
> might be if I were in the process of porting an existing Prolog
> program to Mercury.)

I agree with that. I would also add, in response to an earlier email
in this thread, that while you *can* use both DCGs and state variables
in a single clause, you definitely *shouldn't*. That is just asking for
trouble, in that any future maintainer of that code will curse your name :-(
And that will be true even if that maintainer is an future. version of you.

Zoltan.


More information about the users mailing list