Changes between Version 10 and Version 11 of ExistingRecords


Ignore:
Timestamp:
Mar 29, 2006 2:53:36 PM (8 years ago)
Author:
nhn@…
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • ExistingRecords

    v10 v11  
    6969Note the change in the type of the stored field. 
    7070At the moment, such a record update must be written using the data constructor, not the update syntax. 
    71  
    7271== 'Open' statement == 
    7372 
     
    9897This would insure that by not exporting a field label, it cannot be gotten around by using positional notation. 
    9998This fix would also require the polymorphic setting ability mentioned above and would partially mitigate the need for ReadonlyConstructors 
     99 
     100== Polymorphic Record Update take II == 
     101 
     102(The following was discussed briefly on the Haskell' list.) 
     103Consider the following data type: 
     104{{{ 
     105data T a 
     106  = C1 { f1 :: a } 
     107  | C2 { f1 :: a, f2 :: Int } 
     108  | C3 { f2 :: Int } 
     109deriving Show 
     110}}} 
     111Suppose we want to update the field `f1` only in such a way that 
     112its type changes. We cannot use the record update syntax, as not 
     113all constructors have a field `f1`. So we write a utility function. 
     114However, we would prefer to do as little as possible when it 
     115comes to values constructed by constructors NOT having a field 
     116`f2`. One might naively try this: 
     117{{{ 
     118foo :: T a -> T Int 
     119foo x@(C1 {}) = x {f1 = 1} 
     120foo x@(C2 {}) = x {f1 = 2} 
     121foo x         = x 
     122}}} 
     123But of course, this does not type check as the type of `x` is 
     124different on the LHS and RHS. We can get around that by reconstructing 
     125the value on the RHS: 
     126{{{ 
     127foo :: T a -> T Int 
     128foo x@(C1 {})       = x {f1 = 1} 
     129foo x@(C2 {})       = x {f1 = 2} 
     130foo x@(C3 {f2 = n}) = C3 {f2 = n} 
     131}}} 
     132However, this is bad, because we have to change the code if further 
     133constructors are added, even when they do not have a field `f1`, 
     134and we also have to change the code if further fields are added 
     135to constructors not having the field `f1`. This is tedious, 
     136error prone, and really defeats one of the main reasons for using 
     137records in the first place. For example: 
     138{{{ 
     139data T a 
     140  = C1 { f1 :: a } 
     141  | C2 { f1 :: a, f2 :: Int } 
     142  | C3 { f2 :: Int, f3 :: Char } 
     143  | C4 { f2 :: Int } 
     144  deriving Show 
     145 
     146foo :: T a -> T Int 
     147foo x@(C1 {})               = x {f1 = 1} 
     148foo x@(C2 {})               = x {f1 = 2} 
     149foo x@(C3 {f2 = n, f3 = c}) = C3 {f2 = n, f3 = c} 
     150foo x@(C4 {f2 = n})         = C4 {f2 = n} 
     151}}} 
     152One might think it would be possible to do better if we're furtunate 
     153enough to have a field that is common to *all* constructors not having 
     154a field `f1`, as is the case for `f2` in this case: 
     155{{{ 
     156foo :: T a -> T Int 
     157foo x@(C1 {}) = x {f1 = 1} 
     158foo x@(C2 {}) = x {f1 = 2} 
     159foo x         = x {f2 = f2 x} 
     160}}} 
     161But this does not type check, and it would not apply anyway if 
     162there is no such common field. 
     163 
     164What we really need is a function that reconstructs a value of type `T a` 
     165at type `T b` for all values constructed by a constructor that does not have 
     166a field `f1`: 
     167{{{ 
     168coerce_no_f1 :: T a -> T b 
     169coerce_no_f1 x@(C3 {f2 = n, f3 = c}) = C3 {f2 = n, f3 = c} 
     170coerce_no_f1 x@(C4 {f2 = n})         = C4 {f2 = n} 
     171 
     172foo :: T a -> T Int 
     173foo x@(C1 {}) = x {f1 = 1} 
     174foo x@(C2 {}) = x {f1 = 2} 
     175foo x         = coerce_no_f1 x 
     176}}} 
     177But we'd rather not have to write such functions by hand, just as 
     178we'd rather not write update functions by hand. Maybe the record 
     179update syntax could be extended so that the function that gets 
     180generated behind the scenes only includes constructors that 
     181does NOT mention a particular field. For example, the field 
     182name(s) that must not occur could be prefixed by `~` which suggests 
     183negation in some settings. It does not have this connotation in Haskell, 
     184but at least `~` is already a special symbol. We could then write: 
     185{{{ 
     186foo :: T a -> T Int 
     187foo x@(C1 {}) = x {f1 = 1} 
     188foo x@(C2 {}) = x {f1 = 2} 
     189foo x         = x {~f1} 
     190}}} 
     191Now the code for `foo` only has to be changed if new constructors 
     192having a field `f1` are added. 
     193 
     194Of course, it should be possible to combine this with the normal 
     195record update syntax. E.g. 
     196{{{ 
     197foo :: T a -> T Int 
     198foo x@(C1 {}) = x {f1 = 1} 
     199foo x@(C2 {}) = x {f1 = 2} 
     200foo x         = x {~f1, f2 = f2 x + 1} 
     201}}} 
    100202 
    101203= Meta-Proposal =