[m-dev.] a conditional field update operator

Zoltan Somogyi zoltan.somogyi at runbox.com
Wed Mar 8 15:13:16 AEDT 2017



On Wed, 8 Mar 2017 14:45:53 +1100, Michael Day <mikeday at yeslogic.com> wrote:

> Hi Zoltan,
> 
> > In many cases, the second approach is appropriate, but at the moment,
> > it takes five lines of code instead of one. I was thinking that we could
> > have a new operator, maybe ::= or :?=, that would be like :=, but
> > would create a new structure only if the new value of the field was
> > different from the old value. Then the compiler would expand the one line
> > to the five lines version internally.
> 
> I would be tempted to just make := always do the pointer equality test 
> and omit it if it's obviously going to fail, eg. if the right-hand side 
> is a newly constructed cell that can't possibly be equal.

In most of the cases in which we now use the test-and-only-assign-if-
the-old-and-new-values-are different technique, or conditional-assign
for short, the new value is constructed somewhere else: either in the caller
of the current procedure, or in one of its callees. In procedures that *do*
decide the new value of the field themselves, the code already tends to
follow the pattern of

   ( if ... some test ... then
       compute new value
       assign the new value to !Info ^ fieldname (unconditionally)
   else
       don't assign to !Info ^ fieldname at all
   )

In this case, the old and new values of the field will be the same only
by accident, and in many cases we (humans) can rule out the accident,
because often the new value is computed from the old value by an
operation whose output is necessarily different than its input (e.g.
appending an element to the front of a list). Having the computer
arrive at the same decision would, in many cases, require nontrivial
program analysis.

The reason why I want to preserve the ability of programmers
to ask for the current behavior of := is that in cases like this,
where you know that the "are the old and new values the same?"
test won't succeed often, or at all, the cost of the test is effectively
"distributed fat": a cost imposed on one part of the program
just because *another* part of the program may benefit from it.
Avoiding distributed fat whereever possible is one of the core principles
of the project. In this case, avoiding it is trivial.

One other consideration is that code that updates more than one field,
such as

   !Info ^ field1 := F1,
   !Info ^ field2 := F2

can be expanded one goal at a time. The parser will expand this
into code that constructs Info1 from Info0, and then Info2 from Info1,
and the common struct optimization pass will come along later,
process the resulting conjunction of unifications, and construct
Info2 directly from Info0, optimizing away the materialization of Info1.

For a sequence of two or more *conditional* assignments to fields,
this won't work, because the expansion of a conditional assignment
is not a flat conjunction of unifications. Either the parser must expand
adjacent field updates to the same state variable together (even if
the code does not use state variable syntax), or we need to make
the common struct optimization understand the more complex
code structures that result from their one-by-one expansion.

Does Prince make extensive use of conditional assignment?
Was that the motivation behind your proposal?

Zoltan.



More information about the developers mailing list