Changes between Version 34 and Version 35 of Records/OverloadedRecordFields/Plan


Ignore:
Timestamp:
Aug 30, 2013 1:29:06 PM (2 years ago)
Author:
adamgundry
Comment:

scope issues

Legend:

Unmodified
Added
Removed
Modified
  • Records/OverloadedRecordFields/Plan

    v34 v35  
    275275== Design choices ==
    276276
    277 == Unambiguous fields ==
     277=== Scope issues, or, why we miss dot ===
     278
     279Consider the following example:
     280
     281{{{
     282f :: (Has r "g" Int) => r -> Int
     283f x = g x + 1
     284}}}
     285
     286Q1. What happens if `g` is not in scope?
     287
     288A. The code gives an error. This is where dot-notation (or another syntactic form marking a field name) is better: `f x = x.g + 1` can work even if `g` is not in scope. Observe that something similar happens with implicit parameters: `f y = y + ?x` works even if `x` is not in scope, and introduces a new constraint `(?x :: Int)`.
     289
     290Q2. What if we add `data T = MkT { g :: Char }`?
     291
     292A. The code compiles correctly, even though the datatype is "obviously" irrelevant because the field `g` it declares has the wrong type, so it cannot be selected. This would not be the case if we treated `g` as an unambiguous reference to the only field of that name in scope.
     293
     294Q3. What if we subsequently add another datatype with a field `g`?
     295
     296A. The code still compiles correctly.
     297
     298An advantage of distinguishing record projections syntactically (as in `x.g`) is that `g` is always treated as a record field, regardless of what is in scope. This allows better separation of concerns, as functions that manipulate records can be defined abstractly rather than referring to particular datatypes. We could consider using an operator less controversial than dot (for example, `(|:)` has been suggested), or a keyword such as '''select''':
     299
     300{{{
     301f x = x|:g + 1
     302f x = select g x + 1
     303}}}
     304
     305
     306=== Introducing field names ===
     307
     308As noted above, sometimes one might want to write code that uses record fields without any particular record types being in scope. One workaround is to define unused types with the appropriate field names. This is slightly odd! We might consider adding a new declaration form, say '''field''' `g`, which declares `g` as a record field that is always polymorphic, rather like the function declaration
     309
     310{{{
     311g :: r { g :: t } => r -> t
     312g = field
     313}}}
     314
     315but with the property that it will not clash with actual `g` fields.
     316
     317
     318=== Unambiguous fields ===
    278319
    279320What if `foo` occurs in an expression, and there is only one datatype `T` with a field `foo` in scope? There are three obvious choices:
     
    321362
    322363Since the selectors are hidden by clients (on import) rather than on export, fields can still be used for record update and mentioned in import and export lists, to control access to them (as discussed in the [wiki:Records/OverloadedRecordFields/Plan#Representationhiding representation hiding] section).
    323 
    324 
    325 === Introducing field names ===
    326 
    327 An advantage of distinguishing record projections syntactically (as in `e.x`) is that `x` is always treated as a record field, regardless of what is in scope. This allows better separation of concerns, as functions that manipulate records can be defined abstractly rather than referring to particular datatypes.
    328 
    329 One workaround is to define unused types with the appropriate field names. This is slightly odd, and we might consider adding a new declaration form, which declares `x` as a record field that is always polymorphic, rather like the function declaration
    330 
    331 {{{
    332 x :: r { x :: t } => r -> t
    333 x = field
    334 }}}
    335 
    336 but with the property that it will not clash with actual `x` fields.
    337364
    338365