Functional record update

From Successor ML

Jump to: navigation, search



We propose adding a simple form of functional record update.

In the presence of Functional record extension and row capture, functional record update (i.e., the replacement of existing fields) can be expressed as a derived form.

Motivation and Example

When using records, it is often necessary to construct new records from existing ones, by changing only a small number of fields. For example, this happens when using records to express functional objects, or in the use of records to encode default arguments. Currently, SML provides no convenient way to express this.

TODO: nice example

We propose supporting record update directly with a new syntactic form {atexp where exprow}. The keyword where has been chosen such that it plays a similar role as it does in the signature language.

Alternative choices of syntax include exp where {exprow}. However, the current proposal has several minor advantages:

  1. It adheres to the principle of least surprise. The alternative syntax is likely to make users believe that they can actually write exp_1 where exp_2, for arbitrary record concatenation, and hence may lead to to confusion and complaints, particularly among novices. On the other hand, the proposal does not preclude adding such a general record concatenation construct later (at which point update could be redefined as a derived form).
  2. It is more economic, since it does not require addition of a new syntactic form, only simple extension of the existing record expression syntax.
  3. It is more convenient, because it requires less parentheses. In particular, it nicely generalises the use of records to emulate keyword arguments, as in
     f {a=2, b=3, c=4, d=1}
to default/optional arguments:
     f {defaults where b=3, d=1}
With the alternative syntax above, the latter would become:
     f (defaults where {b=3, d=1})

Moreover, an analogous syntax has already been established in Objective Caml (albeit using the "with" keyword instead of "where").

A minor disadvantage of the syntax is that it requires the nested expression to be restricted to an atomic expression, unless one is willing to give up LR(1) parsing. That forces (probably unexpectedly) use of parentheses in case the expression is an application.




Defined by the following modification to the Definition:

  • In Section 2.8, Figure 4, and Appendix B, Figure 20, change the production for records to:
   { <atexp where> <exprow> }    record

Static Semantics

Defined by the following modification to the Definition:

  • In Section 4.7, change the production for records to:
   [nexp ::=] {<nexp where> <nexprow>}
  • In Section 4.10, Atomic Expressions, change rule 3 to:
   <C |- exprow => rho>    <<C |- atexp => <rho +> rho' in Type    <Dom rho \cap Dom rho' = 0>>>
   ---------------------------------------------------------------------------------------------   (3)
               C |- { <<atexp where>> <exprow> } => {} <+ rho> <<+ rho'>> in Type
  • In Section 4.11, 1st bullet, add the following sentence:
   Similarly, for each occurence of a record update expression, i.e. a record expression
   of the form {atexp where <exprow>}, the program context must determine uniquely the
   domain of its row type, i.e. the labels of the fields that are not enumerated in exprow.

Note that the typing rule does not allow extension of the record. To avoid problems with type inference, adding new fields should be kept as a separate construct (see Functional record extension and row capture). In fact, record update can be expressed as a combination of row capture and record extension, so in the presence of the latter, the former can be defined as a derived form.

For the rule above, performing type inference is straightforward.

Dynamic Semantics

Defined by the following modification to the Definition:

  • In Section 6.7, Atomic Expressions, change rule 92 to:
         <<E |- atexp => r' in Val>>    <E |- exprow => r>
   -------------------------------------------------------------   (92) 
   E |- { <<atexp where>> <exprow> } => {} <<+ r'>> <+ r> in Val




This is a conservative extension. It is already available in Alice ML. A similar feature exists in Objective Caml.


Straightforward in the given form, but may be more intricate with respect to type inference if generalised to support record extension.

Personal tools