[Haskell] Records

Barney Hilken b.hilken at ntlworld.com
Sun Sep 16 13:10:37 EDT 2007


Now that I have a version of ghc with type classes, I have had a go  
at implementing records based on the ideas I mentioned on this list a  
few months ago. The code of my first attempt is available at http:// 
homepage.ntlworld.com/b.hilken/files/Records.hs

I am releasing this to get feedback. I think Haskell needs a records  
system of this kind of generality, and this code at least allows you  
to play around.

 From the comment section of the file:
-----------------------

     Record construction:

         EmptyRec        is the empty record.
         N =: x          is the record with one field labelled N  
carrying data x.
         t +: u          is the union of records t and u. Any overlap  
of labels gives a static error.

     Record destruction:

         t .: N          is the value of field N in record t. A lack  
of field N gives a static error.
         t -: N          is record t with field N deleted. A lack of  
field N gives a static error.

     Record update:

         t |: u          is the record with fields from u where it  
has them, t otherwise. If u has
                         any fields not in t, or of different types  
from t, there is a static error.
                         Note that the result has the same type as t.

     All these records have types:

         EmptyRec        is the type of the empty record.
         N :=: a         is the type of a record with one field  
labelled N carrying data of type a.
         r :+: s         is the union of record types r and s. Any  
overlap of labels gives a static error.
         r :.: N         is the type of field N in a record of type  
r. A lack of field N gives a static error.
         r :-: N         is record type r with field N deleted. A  
lack of field N gives a static error.

     Finally some classes to govern the polymorphism:

         r `Contains` N      means that r is a record type with a  
field labelled N.
         r `Disjoint` s      means that r and s are record types with  
no fields in common.
         r `Subrecord` s     means that r and s are record types, and  
every field of r also occurs in s (with the same type).

     The types of the basic operators are as follows:

         (=:) :: n -> a -> n :=: a
         (+:) :: r `Disjoint` s => r -> s -> r :+: s
         (.:) :: r `Contains` n => r -> n -> r :.: n
         (-:) :: r `Contains` n => r -> n -> r :-: n
         (|:) :: r `Subrecord` s => s -> r -> s

----------------------------------

Note that these records are a lot more expressive than the Hugs  
system, as you can not only extend records by adding fields, but also  
take unions of arbitrary (disjoint) records.

Record update is designed for functions with lots of named optional  
arguments. If you define

	f opts = ... options.:Optj ...
		where
		options = (Opt1 =: val1 +: ... +: Optn =: valn) |: opts

then the user can write (for example):

	f (Optk =: u +: Optl =: v)

to set just two of the options, leaving the rest as default. This  
also cannot be done in the Hugs system.


The main disadvantage of the current implementation is that you have  
to tell the compiler in which order to store the fields, by defining  
one of the following:

    type instance NameCmp N M = NameLT
    type instance NameCmp N N = NameEQ
    type instance NameCmp N M = NameGT

for each pair of labels N & M in such a way as to give a linear order  
on labels. You need n^2 definitions, where n is the number of labels.  
I would do this in Template Haskell, but it won't yet allow you to  
declare type instances. Maybe some compiler support?

Error messages tend to be cryptic. They mostly complain of missing  
instances, and can run to several pages. There is really no way to  
improve this without building it all in to the compiler!


All comments gratefully received, including suggestions on syntax,  
choice of operators, implementation, explanation, etc.


Barney.




More information about the Haskell mailing list