[Haskell-cafe] Type Directed Name Resolution

Richard O'Keefe ok at cs.otago.ac.nz
Sun Nov 14 17:39:56 EST 2010

On 12/11/2010, at 6:06 PM, Sebastian Fischer wrote:
> As others have pointed out, type classes are insufficient for overloading record labels because they do not cover record updates.
> How can we add a special kind of overloading for record labels that also works for updates? Maybe like this:
>    rename :: ((name :: String) @ a) => a -> a
>    rename newName someRecord = someRecord { name = newName }

Why is nobody talking about records in Clean?
Clean is a Haskell-like language, indeed, the latest feature in Clean
is an additional front-end so it can compile Haskell directly.

Adapting section 5.2 from the Clean 2.1 language specification:

	A record type is ... an algebraic data type [with] exactly one
	constructor.  ... a field name is attached to each of [its]
	arguments.  Records cannot be used in a curried way.
	... selection [is] by field name.  When a record is created
	all arguments of the constructor have to be [provided] but
	... in any order.  ... When pattern matching ... on a record,
	one [need only] mention those fields one is interested in.
	A record can be created via a functional update [where] one
	[need only] specify the values for this fields that differ
	from the old record.

	RecordTypeDef = '::' TypeLhs '=' [UniversalQuantVariables]
			'{' (FieldName '::' [Strict] Type)-list '}'

	... The semantic restrictions [of] algebraic data types also
	hold for record types.  The field names inside one record all
	have to be different.  It is allowed to use the same field name
	in different records.

	Record = RecordDenotation | RecordUpdate
	RecordDenotation = '{' [TypeName '|'] (FieldName '=' Expr)-list '}'

	A record can only be used if its type has been defined ... the
	field names must be identical to the field names ... in the
	corresponding type.  ... The order in which the record fields
	are instantiated is irrelevant, but all fields have to get a
	value [of the right type].  ... When creating a record, its
	type [name] can be used to disambiguate [the type]; the type
	constructor can be left out if there is at least one field name
	[that is peculiar to the type].

There's a little gotcha there:
	::T1 = {x :: Int, y :: Int}
	::T2 = {y :: Int, z :: Int}
	::T3 = {z :: Int, x :: Int}
	{x = 1, y = 2}
neither x by itself nor y by itself uniquely determines a record type,
so Clean 2.1 wanted {T1 | x = 1, y = 2} here.  I don't happen to have
a copy of the current manual handy, so I don't know if they've fixed this yet.

	RecordUpdate = '{' [TypeName '|'] [RecordExpr '&']
			[(FieldName Selection... '=' Expr)-list] '}'
	Selection = '.' FieldName | '.' '[' Expr-list ']'

	The record written to the left of the '&' ... is the record to be
	updated.  On the right [of] the '&' are specified the structures
	in which the new record differs from the old one.  A structure
	an be any field of the record or a selection of any field or array
	elements of a record or array stored in this record.   Notice that
	the functional update is not an update in the classical,
	destructive, sense since a new record is created.  The functional
	update of records is performed very efficient[ly] [so] that we
	have not added support for destructive updates of records of
	unique type.  The '&' operator is strict in its arguments.

	RecordSelection = RecordExpr ['.'TypeName] '.'FieldName Selection...
	                | RecordExpr ['.'TypeName] '!'FieldName Selection...

The "!" alternative has to do with Clean's uniqueness typing.

	An object of [a record] type ... can be specified as [a] pattern.
	Only those fields [whose] contents one would like to use [on] the
	right hand side need to be mentioned in the pattern.

	RecordPattern = '{' [TypeName '|'] (FieldName ['=' Pattern])-list '}'

	The type of the record must have been defined ... .  The field names in
	the pattern must be identical to the field names [in that definition].
	... The [TypeName] can only be left out if there is at least one
	field name [which is not defined in any other record type].

See the T1, T2, T3 example above; I repeat that I haven't checked the latest
manual and don't know if the obvious fix has been made yet.

By the way, Clean can freely use '.' for selection because it uses 'o' for
function composition.

I remind readers once again that the latest Clean release compiles Haskell
as well as Clean, so there is a sense in which records like this *are*
available to some Haskell programmers.  Do they meet the needs that people
have been expressing here?  There's no subtyping, but then, I have reasons
for not using CAML.

The only reason I don't use Clean is that I can't.  Clean's original home
was MacOS, but they moved to Windows.  The latest stable release for the
Mac is 32-bit only, PowerPC only, and 4 years old.  Solaris has been
abandoned completely.  Only Windows is seeing any active support.

This has nothing to do with records.  Since Clean's type system is so
very like the Haskell98 type system (except that their prelude breaks
the numeric type classes down to the level of single operations, so
that there is a + class and a * class and so on), this record system
would seem to be pretty much compatible with Haskell.

More information about the Haskell-Cafe mailing list