[m-dev.] for review: record syntax [2]

Peter Schachte schachte at cs.mu.OZ.AU
Wed Jan 5 18:41:47 AEDT 2000


Hi Simon,

I'm only reviewing the documentation changes.

On Tue, Jan 04, 2000 at 05:06:26PM +1100, Simon Taylor wrote:
...
> --- reference_manual.texi	1999/12/13 13:30:47	1.162
> +++ reference_manual.texi	2000/01/04 05:27:34
> + at node Record syntax
> + at subsection Record syntax
> +
> +Record syntax provides a convenient way to extract or update fields
> +of data constructors, independent of the definition of the constructor.

I found the division of material between this section and the Field
access functions section a bit confusing.  From the names, I'd expect
this section to just cover adding labels to type declarations, and the
fact that doing so generates a couple of functions for you, and the
other section to cover use of those functions.

Much of what is said below is said better in the "Field access
functions" section.  You may be able to remove much of the following in
this section and yield a clearer document.  But I'll comment on this
stuff anyway, in case you want to keep it or move it to that section.

> +A field within a term is specified by a list of
> +(possibly module-qualified) field names separated by the operator `^'.
> +For example @samp{field1}, @samp{module__field1} and
> + at samp{field1 ^ field2} are all valid field specifiers.

I think it's easier to understand this if you just say that

  @meta{expression} @samp{^} @meta{field}

is an expression specifying the @meta{field} field of the term
specified by @meta{expression}, and that naturally, you can chain
these.

In the discussion below, I think field names should be lower case, and
in the @meta{} face, since they're meta variables standing for
function names, unless this contradicts existing practise in the
manual.

> +Record syntax expressions are transformed into sequences of calls
> +to field extraction or update functions (@pxref{Field access functions}).
> +
> + at table @code
> + at item @var{Term} ^ @var{Field1} ^ @dots{} ^ @var{FieldN}
> +
> +A field extraction.
> + at var{Term} must be a valid data-term.
> + at var{Field1} @dots{} @var{FieldN} must be valid field names.

As I understand it, this isn't quite true.  Each FieldN may also be a
function.  It may be clearer if you just come out and say that
@samp{^} is just an alternative syntax for function application, and
that specifying a field name in a type declaration just defines a
unary accessor function.  This would be a good clarifying explanation
after introducing this as above.

One detail could use clarification:  Can you write

	Foo ^ bar(3)

and have that turn into

	bar(3, Foo)?

That flexibility would be nice and consistent, though it's not
crucial.  If it's not handled, though, that should probably be
mentioned.

Also, how about

      Accessor = fieldname,
      Variable = Foo ^ Accessor?

(I know you can't handle this on the left of a := goal, but you could
handle it otherwise.)  Again, I think it would be good if it were
allowed, but it's not essential.  And again, if it's not allowed, it
should be mentioned.

...

> + at item @var{Term} ^ @var{Field1} ^ @dots{} ^ @var{FieldN} := @var{FieldValue}
> +
> +A field update.

Before seeing this general, hairy example, I think it would be gentler
to give a simple

	Rec1 = Rec0^salary := Newsalary

sort of example.

> + at var{Term} must be a valid data-term.
> + at var{Field1} @dots{} @var{FieldN} must be valid field names.
> +
> +This is equivalent to the code:
> + at example
> +OldField1 = Term ^ Field1,
> +OldField2 = OldField1 ^ OldField2,

s/OldField2/Field2/

> + at dots{}

I think it would help to insert the line

	OldField_N_Minus_1 = OldField_N_Minus_2 ^ Field_N_Minus_2,

> +NewField_N_Minus_1 = 'FieldN:='(OldField_N_Minus_1, FieldValue),
> + at dots{}
> +NewField1 = 'Field2:='(OldField1, NewField2),
> +Result = 'Field1:='(Term, NewField1)
> + at end example

...

> +The arguments of constructor definitions may be labelled.
> +These labels can be used to conveniently extract and update fields
> +of a constructor in a manner independent of the definition of the
> +constructor (@pxref{Field access functions}). A labelled argument
> +is of the form `@code{@var{FieldName} :: @var{Type}}'.

I think you should mention that for each label @meta{label}, a
function @meta{label}/1 and a function @meta{label}@code{:=}/2 are
generated.  I assume that if this type declaration appears in an
interface section, then these functions are exported, too?  This
should be mentioned here.

Also, you should discuss the determinism of these functions (again,
this is discussed in another section, but the section on record
declarations seems like the place to discuss what code gets generated
for a record declaration).  For example, is this allowed:

	 :- type list(T) ---> [] ; [ head::T | tail::list(T) ].

If so, then head/1 and tail/1 are semidet.  This means one can define
member/2 as:

      member(E, L) :- L^head = E ; member(E, L^tail).

Also, how about this:

	:- type employee --->
		indian(name::string, salary::float)
	    ;	chief(name::string, salary::float,
	    	      manages::list(employee))

Probably a bad example, but I hope you see what I'm doing.  This seems
clear enough and quite useful.  In this case, name/1 and salary/1
should be det, but manages/1 would be semidet.  Again, this should be
discussed.

...

> + at node Field access functions
> + at section Field access functions
> +
> +Fields of constructors of discriminated union types may be
> +labelled. 

Cross reference to record declaration section.

Also, I think you should mention in this section that ':=' is just
syntactic sugar, and that the expression on the left of the := must be
a function application (usually written using ^ notation), and that
the function name must be visible in the source file (known at compile
time).

> These labels can be used to extract and update individual
> +fields of a constructor a manner independent of the definition of

s/constructor a/term in a/

> + at example
> +:- func 'field1:='(type1, int) = type1.
> +
> +'field1:='(Term, Field1) = 'builtin field1:='(Term, Field1) :-
> +        ( Field1 < 0 ->
> +                error("'field1:=': negative value for field1")
> +        ;
> +                true
> +        ).
> + at end example

I don't really see the value in this 'builtin ...' mechanism.
Wouldn't it be just as simple for the user to give the field a
different name and define the functions using that name?  That would
avoid having to explain all this (and the confusion of your earlier
example showing field1/1, field2/1, 'field1:='/1, and 'field2:='/1
functions being generated, but no 'builtin ...' versions of these,
which I would have expected).

-- 
Peter Schachte                     The use of COBOL cripples the mind; its
mailto:schachte at cs.mu.OZ.AU        teaching should, therefore, be regarded
http://www.cs.mu.oz.au/~schachte/  as a criminal offense.
PGP: finger schachte at 128.250.37.3      -- E. W. Dijkstra 
--------------------------------------------------------------------------
mercury-developers mailing list
Post messages to:       mercury-developers at cs.mu.oz.au
Administrative Queries: owner-mercury-developers at cs.mu.oz.au
Subscriptions:          mercury-developers-request at cs.mu.oz.au
--------------------------------------------------------------------------



More information about the developers mailing list