... | ... | @@ -11,7 +11,7 @@ See also the [ high-level summary of the current plan on the Well-Typed blog](ht |
|
|
### Use existing Haskell records
|
|
|
|
|
|
|
|
|
The `OverloadedRecordFields` extension permits existing Haskell records to use duplicate field labels. Thus the following is legal in a single module:
|
|
|
The `AllowDuplicateRecordFields` extension permits existing Haskell records to use duplicate field labels. Thus the following is legal in a single module:
|
|
|
|
|
|
```wiki
|
|
|
data Person = Person { personId :: Int, name :: String }
|
... | ... | @@ -25,7 +25,7 @@ While we might choose to add anonymous records later, they are not central to th |
|
|
|
|
|
- abstraction and representation hiding work just as in normal Haskell: if a field selector is not exported, client code cannot observe it;
|
|
|
|
|
|
- application code can use `OverloadedRecordFields` even with libraries that do not;
|
|
|
- application code can use `AllowDuplicateRecordFields` even with libraries that do not;
|
|
|
|
|
|
- no new declaration syntax is added.
|
|
|
|
... | ... | @@ -35,13 +35,13 @@ For each field in each record datatype, regardless of whether the extension is e |
|
|
### No changes to record selectors, construction or update
|
|
|
|
|
|
|
|
|
Bare uses of the field refer only to the selector function, and work only if this is unambiguous. Thus, in the above example `name :: Person -> String` but bare use of `personId` leads to a name resolution error. This means that turning on `OverloadedRecordFields` for an existing module is a conservative extension: since the module can have no duplicate field names, everything still works. Moreover, changes to GHC's renamer should be minimal. In addition, uses of fields that are always unambiguous (because they mention the constructor, e.g. construction and pattern-matching) may freely use duplicated names.
|
|
|
Bare uses of the field refer only to the selector function, and work only if this is unambiguous. Thus, in the above example `name :: Person -> String` but bare use of `personId` leads to a name resolution error. This means that turning on `AllowDuplicateRecordFields` for an existing module is a conservative extension: since the module can have no duplicate field names, everything still works. Moreover, changes to GHC's renamer should be minimal. In addition, uses of fields that are always unambiguous (because they mention the constructor, e.g. construction and pattern-matching) may freely use duplicated names.
|
|
|
|
|
|
|
|
|
Even though a field label is duplicated in its defining module, it may be possible to use the selector unambiguously elsewhere. For example, another module could import `Person(personId)` but not `Address(personId)`, and then use `personId` unambiguously. Thus it is not enough simply to avoid generating selector functions for duplicated fields.
|
|
|
|
|
|
|
|
|
We propose *no change whatsoever to how Haskell 98 records are constructed* (e.g. `MkT { x = 3, y = True }`). Moreover, we propose *no change to how records are updated*, which remains monomorphic (e.g. `t { y = False }`). If there are many `y` fields in scope, the type of `t` must fix which one is intended. This is a soft spot, but there is really no way around it because Haskell's type-changing update requires modifying multiple fields simultaneously.
|
|
|
We propose *no change whatsoever to how Haskell 98 records are constructed* (e.g. `MkT { x = 3, y = True }`). Moreover, we propose *no change to how records are updated*, which remains monomorphic (e.g. `t { y = False }`). If there are many `y` fields in scope, the type of the context must fix which one is intended, or a type annotation must be supplied. This is a soft spot, but there is really no way around it because Haskell's type-changing update requires modifying multiple fields simultaneously.
|
|
|
|
|
|
## Part 2: Polymorphism over record fields
|
|
|
|
... | ... | @@ -98,7 +98,7 @@ The "`IV`" stands for "implicit values" (we can argue about the name later). It |
|
|
|
|
|
- Of course the call `(iv @ "x" @ alpha)` gives rise to a constraint `(IV "x" alpha)` which must be satisfied by the context.
|
|
|
|
|
|
- The form `#x` in an expression is only valid with `{-# LANGUAGE ImplicitValues #-}` (which is implied by `OverloadedRecordFields`).
|
|
|
- The form `#x` in an expression is only valid with `{-# LANGUAGE OverloadedLabels #-}` (which is implied by `OverloadedRecordFields`).
|
|
|
|
|
|
- The pretty printer prints `IV "x" t` as `#x::t`.
|
|
|
|
... | ... | @@ -110,7 +110,7 @@ Notice that implicit values might be useful for all sorts of things that are not |
|
|
|
|
|
User code can never (usefully) call `iv` (or `ip`) directly, because without explicit type application there is no way to fix `x`.
|
|
|
|
|
|
**Bikeshedding**: perhaps `OverloadedLabels` or `OverloadedSymbols` would be a better name than `ImplicitValues`? The connection to implicit parameters is nice from an implementation point of view, but I'm not sure how relevant it is to users, and the behaviour is rather more like a souped-up `OverloadedStrings`.
|
|
|
**AMG**: I've switched to the name `OverloadedLabels` rather than `ImplicitValues`. The connection to implicit parameters is nice from an implementation point of view, but I'm not sure how relevant it is to users, and the behaviour is rather more like a souped-up `OverloadedStrings`.
|
|
|
|
|
|
### Overloaded record fields
|
|
|
|
... | ... | @@ -160,7 +160,7 @@ These were previously called `Has` and `Upd`, but I suggest using longer and hop |
|
|
|
|
|
More precisely,
|
|
|
|
|
|
- GHC will generate a `HasField` instance whenever it currently generates a selector. Not every field has a selector today, because of existentials (see [ user manual](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/data-type-extensions.html#existential-records)). Moreover, it will only generate a `HasField` instance if the field type is rank-0, i.e. it has no foralls. (Even rank-1 types are out, because they violate the functional dependency on `HasField`, and higher ranks we would need impredicative polymorphism.)
|
|
|
- GHC will generate a `HasField` instance whenever it currently generates a selector. Not every field has a selector today, because of existentials (see [ user manual](https://downloads.haskell.org/~ghc/latest/docs/html/users_guide/data-type-extensions.html#existential-records)). Moreover, it will only generate a `HasField` instance if the field type is rank-0, i.e. it has no foralls. (Even rank-1 types are out, because they violate the functional dependency on `HasField`, and higher ranks would need impredicative polymorphism.)
|
|
|
|
|
|
- Similarly, GHC will generate a `FieldUpdate` instance only for rank-0 fields, that is, ones with no foralls. (Same reason: impredicativity.) Plus, of course, ones that do not mention an existential variable. There are some slightly subtle conditions about when `FieldUpdate` permits updates to change parameter types, as describe in [the original design](records/overloaded-record-fields/design#).
|
|
|
|
... | ... | @@ -182,7 +182,7 @@ has an ambiguous type variable `b`, and we get terrible type inference behaviour |
|
|
1. a two-parameter class `HasField x r` with a type family `FieldType x r`
|
|
|
|
|
|
|
|
|
Options 2 and 3 were explored in the original `OverloadedRecordFields`, and are pretty much equivalent; we initially thought 2 might give nicer inferred types but in fact they tend to be of the form `HasField x r (FieldType x r)` so we might as well go for the two-parameter class. Option 1 should give prettier inferred types (and be easier to use with the `r { x :: t}` syntactic sugar described below), but the lack of evidence for fundeps, and the inability to mention the type of a field, may be restrictive in hard-to-predict ways. At the moment, this page is written to assume option 3.
|
|
|
Options 2 and 3 were explored in the original `OverloadedRecordFields`, and are pretty much equivalent; we initially thought 2 might give nicer inferred types but in fact they tend to be of the form `HasField x r (FieldType x r)` so we might as well go for the two-parameter class. Option 1 should give prettier inferred types (and be easier to use with the `r { x :: t }` syntactic sugar described below), but the lack of evidence for fundeps, and the inability to mention the type of a field, may be restrictive in hard-to-predict ways. At the moment, this page is written to assume option 3.
|
|
|
|
|
|
### Back to implicit values
|
|
|
|
... | ... | @@ -260,11 +260,18 @@ instance HasField "area" Circle where |
|
|
|
|
|
Now `#area` behaves like a virtual record selector; it is not a field of `Circle` or `Triangle` but you can treat it just like one.
|
|
|
|
|
|
|
|
|
The exact rules for when user-defined instances are legal will require some care, because to avoid incoherence/soundness issues we must ensure that they do not clash with any automatically generated instances. A user-defined instance `HasField x t` is legal if:
|
|
|
|
|
|
- `t` is a data type that has no fields, or
|
|
|
|
|
|
- `x` is a literal string and `t` is a data type that does not have the given field (though it may have other fields).
|
|
|
|
|
|
### Syntax
|
|
|
|
|
|
**Design question**: it's not absolutely necessary to use `#x` for a field. Here are some alternatives:
|
|
|
|
|
|
- We could use `@x`, though that would prevent it being used for explicit type application (which is common practice in writing, even if the extension to permit it in Haskell syntax hasn't made much progress).
|
|
|
- We could use `@x`, though that would prevent it being used for explicit type application (which is common practice in writing, even if the extension to permit it in Haskell syntax hasn't made much progress). This is the syntax used by [ record-preprocessor](http://hackage.haskell.org/package/record-preprocessor).
|
|
|
|
|
|
- We could say "if there is at least one data type in scope with a field `x`, then `x` is treated like `(iv @ "x" @ alpha)`". But I hate it. And it doesn't work for virtual fields like `#area` above.
|
|
|
|
... | ... | @@ -285,7 +292,7 @@ I really really like the similarity between the models, and I think it'd be a pi |
|
|
And would implicit parameters *really* be better (from a software engineering point of view) if we replaced `?x` notation with `import GHC.ImplicitParameters( x )`?
|
|
|
|
|
|
|
|
|
Note that the `#x` form only behaves specially if you have `ImplicitValues` or `OverloadedRecordFields` enabled. So existing libraries that use `#` as an operator will work fine. If you want `OverloadedRecordFields` as well, you'll have to put a space between an infix `#` and its second argument, thus `(a # b)` not `(a #b)`. But that's not so bad. And exactly the same constraint applies with `MagicHash`: you must put a space between the `a` and the `#`, not `(a# b)`. I don't think this is a big deal.
|
|
|
Note that the `#x` form only behaves specially if you have `OverloadedLabels` or `OverloadedRecordFields` enabled. So existing libraries that use `#` as an operator will work fine. If you want `OverloadedRecordFields` as well, you'll have to put a space between an infix `#` and its second argument, thus `(a # b)` not `(a #b)`. But that's not so bad. And exactly the same constraint applies with `MagicHash`: you must put a space between the `a` and the `#`, not `(a# b)`. I don't think this is a big deal.
|
|
|
|
|
|
|
|
|
The downside of the `#x` syntax is that uses of lenses like `foo^.bar.baz` become something like `foo ^. #bar . #baz` or `foo ^. xx #bar . xx #baz` (if we need a combinator `xx` to turn an implicit value into a lens). However, this can be mitigated to some extent by users by making their own definitions `bar = xx #bar; baz = xx #baz`.
|
... | ... | @@ -296,7 +303,7 @@ The downside of the `#x` syntax is that uses of lenses like `foo^.bar.baz` becom |
|
|
This is not necessary to begin with, but we may want `r { x :: t }` to be syntactic sugar for `(HasField "x" r t)`, although this might conflict with syntax for anonymous records. This is easy to desugar in the typechecker, but it is slightly harder to re-apply the sugar in inferred types and error messages. A similar syntax for updates would be nice, but it's not clear what.
|
|
|
|
|
|
|
|
|
In general, it is hard to ensure that we get nice inferred types and error messages that don't mention the type families unless absolutely necessary.
|
|
|
In general, it is hard to ensure that we get nice inferred types and error messages that don't mention the type families unless absolutely necessary. Initially we plan not to implement the syntactic sugar.
|
|
|
|
|
|
### Design extension: anonymous records
|
|
|
|
... | ... | @@ -387,8 +394,6 @@ $sel_T_x (MkT x) = x |
|
|
instance HasField "x" T where
|
|
|
type FieldType "x" T = S
|
|
|
getField _ = $sel_T_x
|
|
|
|
|
|
x = $sel_T_x -- The H98 selector, when OverloadedRecordFields is not on
|
|
|
```
|
|
|
|
|
|
### Reflections
|
... | ... | @@ -421,14 +426,15 @@ It is rather reminiscent of Carlos Camaro's [ System CT](http://homepages.dcc.uf |
|
|
We propose three essentially orthogonal additions to GHC:
|
|
|
|
|
|
1. `HasField` and `FieldUpdate` typeclasses, with special-purpose constraint solving behaviour (just like `Coercible`, we do not require a special extension to enable this, as its effect is limited to code that imports the relevant module);
|
|
|
1. an extension `ImplicitValues` to enable the `#x` syntax, interpreted with the `IV` typeclass;
|
|
|
1. an extension `OverloadedRecordFields` to permit the same field name to be used multiple times in the same module.
|
|
|
1. an extension `OverloadedLabels` to enable the `#x` syntax, interpreted with the `IV` typeclass;
|
|
|
1. an extension `AllowDuplicateRecordFields` to permit the same field name to be used multiple times in the same module.
|
|
|
|
|
|
|
|
|
The `OverloadedRecordFields` extension is then defined as the combination of `OverloadedLabels` and `AllowDuplicateRecordFields`.
|
|
|
|
|
|
|
|
|
These are all useful independently, but complement each other:
|
|
|
|
|
|
- Without either of the extensions, the special typeclasses allow users to write code that works for all datatypes with particular fields (albeit without a nice built-in syntax).
|
|
|
- `-XImplicitValues` uses the special typeclasses through the instance for `IV x (r -> a)`, but is also useful when used at other types (e.g. we could give an instance `IV x (Proxy x)` to allow implicit values to represent Symbol proxies).
|
|
|
- For convenience, `-XOverloadedRecordFields` will enable `-XImplicitValues`, but `-XOverloadedRecordFields -XNoImplicitValues` is a perfectly sensible combination: it allows duplicate field names provided they are not used ambiguously.
|
|
|
|
|
|
**Bikeshedding**: perhaps we should expose an extension called `LiberalRecordFields` that has the behaviour described in part 1 (i.e. permitting duplicate fields but without the new syntax or fancy types of part 2)? Then `OverloadedRecordFields` would just switch on `LiberalRecordFields` and `ImplicitValues`, but `LiberalRecordFields` is a perfectly sensible extension to use on its own. |
|
|
- `OverloadedLabels` uses the special typeclasses through the instance for `IV x (r -> a)`, but is also useful when used at other types (e.g. we could give an instance `IV x (Proxy x)` to allow implicit values to represent Symbol proxies).
|
|
|
- `AllowDuplicateRecordFields` is perfectly sensible without `OverloadedLabels`: it allows duplicate field names provided they are not used ambiguously. |