First class labels

[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? Claus related: - tweak the existing records system (none) http://hackage.haskell.org/trac/haskell-prime/ticket/27 - add overlapping or incoherent instances (probably no) http://hackage.haskell.org/trac/haskell-prime/ticket/54 - add FunctionalDependencies (probably no) http://hackage.haskell.org/trac/haskell-prime/ticket/36 - add multi parameter type classes (probably yes) http://hackage.haskell.org/trac/haskell-prime/ticket/49 ---------------------------------------- {-# 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)

(does replying to my own message constitute a discussion?-) I would have hoped for someone to say any of the following: - no need for that, Haskell' is going to have proper records - Haskell' is going to avoid the records discussion, but I still don't like your idea, because.. - perhaps that idea isn't entirely wrong, let's see.. but, of course, I realise now that I made a fundamental mistake. The established procedure to generate discussion about language design issues is to propose either syntactic sugar or complex type system extensions, and my proposal was careful to do neither:-( To make up for this, I've now extended the example code to include more operations, cryptic infix operators, and more type class tricks (we have examples of record field selection, symmetric record concatenation, record field removal, record field update, record field renaming). Will that be sufficient to encourage one or two responses, even though the proposal is still the same? If nothing else, you might want to look through the code for usage patterns and usage issues wrt type classes/instances and various "extensions". How much of that will Haskell' be able to handle? Will it remove the need for workarounds? Having first-class labels would also simplify and make more useable things like the HList effort, which plays similar tricks, and the particular record system variation we get with the attached code is not unlike the one presented at last year's TFP by Daan. So here are two more related links: Extensible records with scoped labels. (draft) http://www.cs.uu.nl/~daan/pubs.html Strongly typed heterogeneous collections http://homepages.cwi.nl/~ralf/HList/ Cheers, Claus
participants (1)
-
Claus Reinke