Changes between Version 9 and Version 10 of Records/OverloadedRecordFields/Plan


Ignore:
Timestamp:
Jun 19, 2013 8:57:56 AM (10 months ago)
Author:
adamgundry
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Records/OverloadedRecordFields/Plan

    v9 v10  
    33This is a plan to implement overloaded record fields, along the lines of SPJ's [wiki:Records/OverloadedRecordFields Simple Overloaded Record Fields] proposal, as a Google Summer of Code project. (See the [http://www.google-melange.com/gsoc/project/google/gsoc2013/adamgundry/23001 GSoC project details], for reference.) The page on [wiki:Records Records] gives the motivation and many options.  In particular, the proposal for [wiki:Records/DeclaredOverloadedRecordFields Declared Overloaded Record Fields] is closely related but makes some different design decisions. 
    44 
    5  
    6 == Design == 
    7  
    8 '''SLPJ''' this section should be a careful design specfication. 
    95 
    106=== Motivation === 
     
    1713}}} 
    1814 
    19 are declared in the same module, there is no way to determine which type an occurrence of the personId record selector refers to. A common workaround is to use a unique prefix for each record type, but this leads to less clear code and obfuscates relationships between fields of different records. Qualified names can be used to distinguish record selectors from different modules, but using one module per record is often impractical. 
     15are declared in the same module, there is no way to determine which type an occurrence of the `personId` record selector refers to. A common workaround is to use a unique prefix for each record type, but this leads to less clear code and obfuscates relationships between fields of different records. Qualified names can be used to distinguish record selectors from different modules, but using one module per record is often impractical. 
    2016 
    2117Instead, we want to be able to write postfix polymorphic record projections, so that `e.personId` resolves the ambiguity using the type of `e`. In general, this requires a new form of constraint `r { x :: t }` stating that type `r` has a field `x` of type `t`. For example, the following declaration should be accepted: 
     
    2622}}} 
    2723 
     24A constraint `R { x :: t }` is solved if `R` is a datatype that has a field `x` of type `t` in scope. An error is generated if `R` has no field called `x`, it has the wrong type, or the field is not in scope.  
     25 
     26 
     27== Design == 
     28 
     29In the sequel, we will describe the `-XOverloadedRecordFields` extension, which permits multiple field declarations with the same label, introduces new record field constraints and a new syntax for record projection. 
    2830 
    2931 
     
    3941Recall that `Symbol` is the kind of type-level strings. The notation extends to conjunctions:  `r {x :: tx, y :: ty}` means `(Has r "x" tx, Has r "y" ty)`. Note also that `r` and `t` might be arbitrary types, not just type variables or type constructors.  For example, `T (Maybe v) { x :: [Maybe v] }` means `(Has (T (Maybe b)) "x" [Maybe v])`. 
    4042 
    41 Instances for the `Has` typeclass are implicitly generated,  corresponding to fields in datatype definitions, when the flag `-XOverloadedRecordFields` is enabled. For example, the data type 
     43Instances for the `Has` typeclass are implicitly generated,  corresponding to fields in datatype definitions. For example, the data type 
    4244 
    4345{{{ 
     
    5355 
    5456The `(b ~ [a])` in the instance is important, so that we get an instance match from the first two fields only. For example, if the constraint `Has (T c) "x" d` is encountered during type inference, the instance will match and generate the constraints `(a ~ c, b ~ d, b ~ [a])`. 
    55  
    56 If multiple constructors for a single datatype use the same field name, all occurrences must have exactly the same type, as at present. 
    57  
    58 A constraint `R { x :: t }` is solved if `R` is a datatype that has a field `x` of type `t` in scope. An error is generated if `R` has no field called `x`, it has the wrong type, or the field is not in scope.  
    5957 
    6058 
     
    148146=== Record selectors ===  
    149147 
    150 Optionally, we could [wiki:Records/DeclaredOverloadedRecordFields/NoMonoRecordFields add a flag `-XNoMonoRecordFields`] to disable the generation of the usual monomorphic record field selector functions.  This is not essential, but would free up the namespace for other record systems (e.g. '''lens'''). Note that `-XOverloadedRecordFields` will generate monomorphic selectors by default for backwards compatibility reasons, but they will not be usable if multiple selectors with the same name are in scope. 
    151  
    152 When either flag is enabled, the same field label may be declared repeatedly in a single module (or a label may be declared when a function of that name is already in scope). 
    153  
    154 Even if the selector functions are suppressed, we still need to be able to mention the fields in import and export lists, to control access to them (as discussed in the [wiki:Records/OverloadedRecordFields/Plan#Representationhiding representation hiding] section). 
    155  
    156 '''AMG''' perhaps we should also have a flag to automatically generate the polymorphic record selectors? These are slightly odd: if two independent imported modules declare fields with the same label, only a single polymorphic record selector should be brought into scope. 
     148Even with `-XOverloadedRecordFields` enabled, monomorphic record selector functions will be generated by default for backwards compatibility reasons, and for use when there is no ambiguity. They will not be usable if multiple selectors with the same name are in scope. 
     149 
     150Optionally, we could [wiki:Records/DeclaredOverloadedRecordFields/NoMonoRecordFields add a flag `-XNoMonoRecordFields`] to disable the generation of the usual monomorphic record field selector functions.  This is not essential, but would free up the namespace for other record systems (e.g. '''lens'''). Even if the selector functions are suppressed, we still need to be able to mention the fields in import and export lists, to control access to them (as discussed in the [wiki:Records/OverloadedRecordFields/Plan#Representationhiding representation hiding] section). 
     151 
     152We could also add a flag `-XPolyRecordFields` to generate polymorphic selector functions. This implies `-XNoMonoRecordFields`. For example, if a record with field `x` is declared then the function 
     153 
     154{{{ 
     155x :: Has r "x" t => r -> t 
     156x e = e.x 
     157}}} 
     158 
     159would be generated. However, these have slightly odd behaviour: if two independent imported modules declare fields with the same label, they will both generate identical polymorphic selectors, so only one of them should be brought into scope. 
    157160 
    158161 
     
    179182 
    180183If a field has a rank-1 type, the `Has` encoding works fine: for example, 
     184 
    181185{{{ 
    182186data T = MkT { x :: forall a . a -> a } 
    183187}}} 
     188 
    184189gives rise to the instance 
    185 {{{ 
    186 instance b ~ a -> a => Has T "x" b 
     190 
     191{{{ 
     192instance (b ~ a -> a) => Has T "x" b 
    187193}}} 
    188194 
    189195However, if a field has a rank-2 type or higher (so the selector function has rank at least 3), things are looking dangerously impredicative: 
     196 
    190197{{{ 
    191198data T b = MkT { x :: (forall a . a -> a) -> b } 
    192199}}} 
     200 
    193201would give 
    194 {{{ 
    195 instance c ~ (forall a . a -> a) -> b => Has (T b) "x" c 
    196 }}} 
     202 
     203{{{ 
     204instance (c ~ ((forall a . a -> a) -> b)) => Has (T b) "x" c 
     205}}} 
     206 
    197207but this is currently forbidden by GHC, even with `-XImpredicativeTypes` enabled. Indeed, it would not be much use if it were possible, because bidirectional type inference relies on being able to immediately infer the type of neutral terms like `e.x`, but overloaded record fields prevent this. Traditional monomorphic selector functions are likely to be needed in this case. 
    198208