Changes between Version 19 and Version 20 of Records/OverloadedRecordFields/Plan


Ignore:
Timestamp:
Jul 17, 2013 3:18:01 PM (21 months ago)
Author:
adamgundry
Comment:

--

Legend:

Unmodified
Added
Removed
Modified
  • Records/OverloadedRecordFields/Plan

    v19 v20  
    3737=== Projections ===  
    3838 
    39 Record field constraints are introduced by projections. If there are two or more fields `x` in scope, then an occurrence of `x` has type `a { x :: b } => a -> b` instead of generating an ambiguity error. If there is a single field `x` in scope, then it refers to the usual monomorphic record selector (ensuring backwards compatibility). If there are any normal identifiers `x` in scope (as well as fields) then a use of `x` leads to an ambiguity error. 
     39A record field constraints is introduced when a field is used in an expression. If all the `x`s in scope are record fields, then an occurrence of `x` has type `a { x :: b } => a -> b` instead of generating an ambiguity error. If there are any normal identifiers `x` in scope (as well as fields) then a use of `x` leads to an ambiguity error. 
    4040 
    4141 
    4242=== Record field constraints ===  
    4343 
    44 Record field constraints `r { x :: t }` are syntactic sugar for typeclass constraints `Has r "x" t`, where 
    45  
    46 {{{ 
    47 class Has (r :: *) (x :: Symbol) (t :: *) where 
    48   getFld :: r -> t 
    49 }}} 
    50  
    51 Recall 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])`. 
    52  
    53 Instances for the `Has` typeclass are automatically generated (for modules with `-XOverloadedRecordFields` enabled) using the record fields that are in scope. For example, the data type 
     44Record field constraints `r { x :: t }` are syntactic sugar for typeclass constraints `Get r "x" t`, where 
     45 
     46{{{ 
     47type family GetResult (r :: *) (f :: Symbol) :: * 
     48 
     49class t ~ GetResult r f => Get r (f :: Symbol) t where 
     50  getFld :: proxy f -> r -> t 
     51}}} 
     52 
     53Recall that `Symbol` is the kind of type-level strings. The notation extends to conjunctions:  `r {x :: tx, y :: ty}` means `(Get r "x" tx, Get 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 `(Get (T (Maybe b)) "x" [Maybe v])`. 
     54 
     55Instances for the `Get` typeclass and `GetResult` type family are automatically generated (for modules with `-XOverloadedRecordFields` enabled) using the record fields that are in scope. For example, the data type 
    5456 
    5557{{{ 
     
    5759}}} 
    5860 
    59 has the corresponding instance 
    60  
    61 {{{ 
    62 instance (b ~ [a]) => Has (T a) "x" b where 
     61has the corresponding instances 
     62 
     63{{{ 
     64type instance GetResult (T a) "x" = [a] 
     65instance (b ~ [a]) => Get (T a) "x" b where 
    6366  getFld (MkT { x = x }) = x 
    6467}}} 
    6568 
    66 The `(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])`. 
     69The `(b ~ [a])` in the instance is important, so that we get an instance match from the first two parameters only. For example, if the constraint `Get (T c) "x" d` is encountered during type inference, the instance will match and generate the constraints `(a ~ c, b ~ d, b ~ [a])`. Moreover, the `GetResult` type family ensures that the third parameter is functionally dependent on the first two, which is needed to avoid ambiguity errors when composing overloaded fields. 
     70 
     71The reason for using a three-parameter class, rather than just two parameters and a type family, is to support the syntactic sugar. With a two-parameter class we could easily end up inferring types like the following, and it would be hard to reapply the sugar: 
     72 
     73{{{ 
     74f :: (Has r "x", Has r "y", GetResult r "x" ~ Int, GetResult r "y" ~ Int) => r -> Int 
     75f r = x r + y r :: Int 
     76}}} 
    6777 
    6878