First class labels

Claus Reinke claus.reinke at
Tue Feb 7 08:06:48 EST 2006

[The reader will have noticed the absence of the words
 extensible polymorphic records in the subject line]

If the long-overdue overhaul of Haskell's record system
appears to be out of scope for Haskell' (??), I suggest to add 
some form of first-class labels to Haskell' (to be really useful, 
this depends on real type classes, not the cut-down version 
of Haskell 98).

I often find myself coding up my own records, which is fairly
quick and painless, though of course lacking the comfort of
a real records systems (see code at bottom of this message; 
one might add some syntactic sugar, or not).
The two main problems I have with this are 

(a) the need for so-called "extensions" (I see these features 
        merely as removing needless restrictions, and will be
        very disappointed if Haskell' does not provide for 
        them, not even optionally)

(b) the need to declare my labels. it is not so much the
        typing, but:
        - possible namespace overlaps (someone might
            want to use my labels as constructor names)
        - the need for a common import origin (someone
            has to declare all those labels, and all users 
            better agree on who that someone is)

This suggestion is concerned with (b). I propose to add
first class labels to Haskell' (every label is a constant, the 
only inhabitant of its type). I outline three options, of which 
option 1 is my personal favourite, followed by option 3:

Option 1: 
    remove the need to declare labels, make them identifiable 
    as such in use. TREX demonstrates that this is possible.

    in the past (when I was using Hugs more than GHC), I 
    have been known to abuse TREX, not for its records, 
    but for its labels. In other words, I'd use single-field 
    dummy records "(mylabel=())" as labels in constructions 
    like the one at the bottom of this message.

    pro: simple in use, labels have their own namespace,
            no conflicting imports, known to work
    con: need to give up some identifiable space in the
            language for labels

Option 2:
    add sharing imports to the module language. SML
    supports something like that, I think.

    that would allow importers to declare that the various
    imported declarations of a label are not in conflict, but
    refer to the very same type.
    pro: seems like a useful feature anyway
    con: more complex than needed for this proposal,
                and would be very verbose in use

Option 3:
    add a "magic" standard module Data.Label, which 
    implicitly exports all possible labels and only such.

    that would allow all importers to refer to a common
    origin for their label declarations. imports from that
    module would amount to declarations of shared label
    types: "import Data.Label(mylabel1,mylabel2)"

    pro: no syntax extension or separate label namespace,
            no problems with common imports
    con: no separate label namespace, labels still need to
                be declared, by means of import

Either 1 or 3 should be much easier to implement than
full records, and either option would be a small change,
avoiding the main problem of a full records system: that 
noone can agree on what the ultimate form should look 
like. Either option would enable a pragmatic approach
to the problem while we are waiting for the real records.

What do you think?


- tweak the existing records system (none)

- add overlapping or incoherent instances (probably no)

- add FunctionalDependencies (probably no)

- add multi parameter type classes (probably yes)

{-# OPTIONS -fglasgow-exts #-}
{-# OPTIONS -fallow-overlapping-instances #-}

-- poor man's records using nested tuples and declared labels:

class Select label val rec | label rec -> val where
  sel :: rec -> label -> val

instance Select label val ((label,val),r) where
  sel ((_,val),_) label = val

instance Select label val r => Select label val (l,r) where
  sel (_,r) label = sel r label

-- some labels and examples

data A = A deriving Show
data B = B deriving Show
data C = C deriving Show
data D = D deriving Show

r1 = ((A,True),((B,'a'),((C,1),())))

r2 = ((A,False),((B,'b'),((C,2),r1)))

x1 r = (r `sel` A, r `sel` B, r `sel` C)

More information about the Haskell-prime mailing list