Changes between Version 17 and Version 18 of Records/SyntaxDirectedNameResolution


Ignore:
Timestamp:
Mar 12, 2012 2:22:07 AM (2 years ago)
Author:
elaforge
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Records/SyntaxDirectedNameResolution

    v17 v18  
    1515every single reference, as long as you used `#` consistently. 
    1616 
    17 Everything else remains the same.  Records wishing the same name must live in 
    18 separate modules, but field access doesn't have to mention the module names: `(#b . #a) record`.  People 
    19 who like unqualified imports can still use them, since `import A.B.C` will 
    20 bring `A.B.C.a` into scope as well as `a`.  If there are two `a`s from 
    21 separate modules, an unqualified `a` will give an ambiguous error as always, 
    22 but `#a` will use its argument type to automatically qualifiy it to the right 
    23 module.  Since `#a` is desugared to `A.B.C.a` it's a separate symbol from `a` 
    24 and they can both exist in the same module provided, of course, that you 
    25 imported `A.B.C` qualified. 
     17Everything else remains the same.  Field access doesn't have to mention the 
     18module names: `(#b . #a) record`.  People who like unqualified imports can 
     19still use them, since `import A.B.C` will bring `A.B.C.a` into scope as well 
     20as `a`.  If there are two `a`s from separate modules, an unqualified `a` will 
     21give an ambiguous error as always, but `#a` will use its argument type to 
     22automatically qualifiy it to the right module.  Since `#a` is desugared to 
     23`A.B.C.a` it's a separate symbol from `a` and they can both exist in the same 
     24module provided.  This means you can type `let field = #field record`, which 
     25is a pretty common thing to want to do (think of all the `thing = obj.thing` 
     26stuff that shows up in OO languages). 
    2627 
    2728This is enough for field access, but to support convenient modification we 
    28 can use lenses.  If we substitute `Lens` for `f` instead of (->), then 
     29can use lenses.  If we substitute `Lens` for the `f` in the first paragraph 
     30instead of (->), then 
    2931we can write `#field :: Lens M.Rec a -> b` and still have it resolve to `M.field`. 
     32 
     33== examples == 
    3034 
    3135Here's an example using plain function "# resolution": 
     
    200204== pros == 
    201205 
    202  1. No effect on (.) operator, which is composition as always.  No "binds tighter than functions" or left-to-right vs. right-to-left controversy, and partial application works as it always did. 
    203  2. Record declaration syntax remains exactly the same.  Totally backward compatible, we can gradually convert existing programs.  Even convert an existing record field by field, no need for a single giant patch to update everything at once. 
    204  3. Works on any function, so it doesn't tie you to the implementation of a record, you can remove a field and add a compatibility shim.  So no tension between directly exposing the record implementation vs. writing a bunch of set/modify boilerplate. 
    205  4. It's not just record types, any lens can go in the lens composition, e.g. one for Data.Map.  So you can translate imperative `record.a.b[c].d = 42` to `set (#d . Map.lens c . #b . #a) 42 record`.  Make a new operator `(.>) = flip (.)` if you like left to right. 
     206 1. No effect on (.) operator, which is composition as always.  No "binds tighter than functions" or left-to-right vs. right-to-left controversy, and partial application works as it always did.  To compose lenses either hide the Prelude `(.)` and import the Category one, or just write your own lens composition operator and avoid all the `import hiding`. 
     207 2. Record declaration syntax remains exactly the same.  It's totally backward compatible and you can gradually convert existing programs.  Even convert an existing record field by field, there's no need for a single giant patch to update everything at once. 
     208 3. Works on any function, so it doesn't tie you to the implementation of a record.  You can remove or change the type of a field and add a compatibility shim.  So there's no tension between directly exposing the record implementation vs. writing a bunch of set/modify boilerplate. 
     209 4. It's not just record types, any lens can go in the lens composition, e.g. one for Data.Map.  So you can translate imperative `record.a.b[c].d = 42` to `set (#d . Map.lens c . #b . #a) 42 record`.  Make a new operators if you like left to right or want infix modification. 
    206210 5. Module export list controls access over record fields as always. 
    207211 6. Orthogonal to records: any function can be addressed. 
     
    210214== cons == 
    211215 
    212  1. Still can't have two records with the same field name in the same module since it relies on modules for namespacing. 
    213  2. Lenses can't handle updates that change the type, e.g. from `Rec a` to `Rec b`.  If the `set` function is `Lens rec field -> field -> rec -> rec` then you can't change the type of `rec`.  I'm sure if this is solvable without the set being builtin syntax, or if a fancier lens implementation could admit `Lens rec1 rec2 field -> field -> rec1 -> rec2`. 
     216 1. Lenses can't handle updates that change the type, e.g. from `Rec a` to `Rec b`.  If the `set` function is `Lens rec field -> field -> rec -> rec` then you can't change the type of `rec`.  I'm sure if this is solvable without the set being builtin syntax, or if a fancier lens implementation could admit `Lens rec1 rec2 field -> field -> rec1 -> rec2`. 
     217 2. Can't update more than one field at once.  Traditional record syntax is there for this case.  You need it to initialize new records anyway. 
    214218 3. It's another way to resolve a name to a different function body that's not typeclasses.  One way should be enough.  But typeclasses are fundamentally global. 
    215219 4. The function to resolve must be monomorphic, so there is no "structural polymorphism" e.g. `getName :: (Has Name a) => a -> String` 
     
    221225My spin on the cons: 
    222226 
    223  1. I don't mind.  To my mind, modules are there for namespace control and so two things shouldn't be able to have the same name in one module by definition.  I have very few records that really want to have the same field name *and* live in the same module, but for those who have more of those, nested modules would be an orthogonal feature that would satisfy them. You could argue that this is a necessary development, because modules are *the* user-controlled namespace and access control mechanism (typeclasses are always global and hence not that controlled), then the solution must be modules.  Any other solution is either going to be unsatisfactory because you can't control the scope of the names, or unsatisfactory because it's a non-module way of namespacing. 
    224  
    225  2. It's not too satisfying, but you can fall back to `rec { field = ... }`.  In practice I'd define a type specific update function as I do now, to avoid being tied `field` always existing in the record.  Still, it's worth thinking about whether this proposal could later be extended to support type-changing updates if there were further language support. 
    226  
     227 1. You can fall back to `rec { field = ... }`.  In practice I'd define a type specific update function as I do now, to avoid being tied `field` always existing in the record.  Still, it's worth thinking about whether this proposal could later be extended to support type-changing updates if there were further language support. 
     228 2. Same as 1. 
    227229 3. The global-ness of typeclass seems hard to reconcile with the idea of wanting to control export of record fields, so maybe this is unavoidable. 
    228  
    229230 4. I'm not real fond of structural polymorphism anyway, I believe parametric and typeclass polymorphism are more principled. 
     231 
     232Advocacy for modules being the clash-prevention mechanism, rather than 
     233typeclasses: 
     234 
     235Modules are *the* user-controlled namespace and access control mechanism. 
     236Typeclasses are always global and hence cannot naturally admit access control.  Any other 
     237solution is either going to be unsatisfactory because you can't control the 
     238scope of the names, or unsatisfactory because it's invented a new way of namespacing 
     239that's not modules.