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


Ignore:
Timestamp:
Jul 17, 2013 3:18:01 PM (2 years 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