FW: [mercury-users] Records

Richard A. O'Keefe ok at hermes.otago.ac.nz
Wed Nov 10 11:07:18 AEDT 1999


	> 	You can chain the :='s:
	> 	
	> 		R2 = R0 ^f := V1
	> 		        ^g := V2
	> 	
	> Not in the presence of record invariants (Eiffel, I love you, sometimes).
	> 
	> What one _can_ do is form compound names, so that
	> my example above with the guillemets is mapped to
	>     'f:=g:='(X, New_F, New_G)
	> In general, there need not be any 'f:=' and 'g:=' functions that it is
	> composed from.
	
	I thought of that but decided it would get to complex to be workable.
	E.g., if I instead wrote
	
			R2 = R0 ^g := V2
			        ^f := V1
	
	it would look for a different function.

But why?  The rule I had in mind is that the parser would
_sort_ the field names, so that
	R2 = R0 with {f := V1, g := V2}
would be _identical_ to
	R2 = R0 with {g := V2, f := V1}
after parsing.

	I still
	don't want to have to define all the combinations of setters.

Let me give an example that shows why being _able_ to define
combinations is important.

An 'ipath' is a proper path in the lattice of natural numbers.
I want to express this as a data type in a declarative language.
So here goes:

    data ipath
       = edge(
             From: integer where From >= 0,
             To  : integer where To >= 1,
             First is From,
             Last  is To
         ) where From < To
      |
         join(
            Left:  ipath,
            Right: ipath,
            First is First of Left,
            Last  is Last  of Right
	 ) where Last of Left = First of Right
      .

Suppose I have E0=edge(From=1, To=2) and I want to change it
to E1=edge(From=3, To=4).  The assignment
    E1 = E0 with {From=3} with {To=4}
won't work, because the first step will violate the constraint,
but the assignment
    E1 = E0 with {To=4} with {From=3}
will work.  The order in which I have to write the assignments
depends on the values.  But
    E1 = E0 with {From=3, To=4}
and 
    E1 = E0 with {To=4, From=3}
both work.  In fact they are both the same thing.
Ok, in that case, there was a (data-dependent) ordering of
individual assignments that _did_ work.  Let's look at the
other case.
    E2 = edge(From=1, To=2),
    E3 = edge(From=2, To=4),
    E4 = edge(From=1, To=3),
    E5 = edge(From=3, To=4),
    J0 = join(Left=E2, Right=E3)
Suppose I want to update that to join(Left=E4, Right=E5) (another
path between the same end-points).  The order
    J1 = J0 with {Left=E4} with {Right=E5}
won't work, because Last of E4 ~= First of E2.  But the order
    J1 = J0 with {Right=E5} with {Left=E4}
won't work either, because First of E5 ~= Last of E2.
The combined update
    J1 = J0 with {Left=E4, Right=E5}
is no trouble at all.

It should be clear that I am assuming that
	Record with {Field=Value, ...}
first constructs the new record and then checks the constraints.

	For
	many types, many fields can be set independently;

I respectfully suggest that there are not _that_ many such types,
once you stop and work out what the constraints are.  Consider for
example the classic

    data date
       = date(
             Year : integer where 1900 < Year < 2100,
             Month: integer where 1 <= Month <= 12,
             Day  : integer where 1 <= Day <= 31
	     Wday is "day of week, sunday = 0"
	 ) where "it's a valid date in the Gregorian calendar".

Let's consider changing
	D0 = date(Year = 2000, Month = 2, Day = 29)
to
	D1 = date(Year = 2001, Month = 3, Day = 30)

There are 3! ways to express this using single-field-at-a-time changes.
Setting the Year first doesn't work.
Setting the Day first doesn't work.
Setting the Month first works, so 
    D1 = D0 with {Month=3} with {Day=30} with {Year=2001}
and D1 = D0 with {Month=3} with {Year=2001} with {Day=30}
both work, and the other 4 orders don't.

Ok, so now let's try to change D1 _back_ to D0.
This time the Month-first orders *don't* work and the other 4 *do*.
Once again, we see that just which order works depends on the value
of the fields.  To keep the code simple, you need multiple field update.

You might like to know how Java does it.
Java lets you change fields one at a time, has no class invariant
in the language, and the library designers don't seem to be all that
clear on the concept either.  In particular, it will happily let you
construct totally crazy dates.  I'd rather Mercury didn't let you
do that.

	Ok, so you could apply a rule that if the
	combination setter is defined, you use it, if not, you use the
	individual ones.  But what happens when you're setting 3 members, and
	there are setters defined for each individual field and for each
	combination of two?  The compiler could choose any of the 3 pairs
	together with the remaining individual setter, and could apply these
	two setters in either order, for 6 possible choices.  Which one do you
	choose?
	
There's the answer:  YOU choose, not the compiler.  It's an ambiguity.
If the compiler tells you
	R with {A=X, B=Y, C=Z}
is ambiguous between uses of A:=, B:=, C:=, AB:=, BC:=, AC:=,
then YOU choose which one you mean and write it that way:
	R with {A=X} with {B=Y,C=Z}
for example.
--------------------------------------------------------------------------
mercury-users mailing list
post:  mercury-users at cs.mu.oz.au
administrative address: owner-mercury-users at cs.mu.oz.au
unsubscribe: Address: mercury-users-request at cs.mu.oz.au Message: unsubscribe
subscribe:   Address: mercury-users-request at cs.mu.oz.au Message: subscribe
--------------------------------------------------------------------------



More information about the users mailing list