... | ... | @@ -12,6 +12,9 @@ Following the 2015 redesign, we have three separate components to implement, whi |
|
|
- the `ImplicitValues` extension, to enable the `#x` syntax;
|
|
|
- the `HasField` and `FieldUpdate` typeclasses, with special-purpose constraint solving behaviour, which do not require a language extension.
|
|
|
|
|
|
|
|
|
As of March 2015, work is progressing on the implementation, and Part 1 (the simplified `OverloadedRecordFields` extension) is nearly complete. Once it is ready, a Phab diff will be opened for review, then parts 2 and 3 will be worked on. Note that all the parts are useful in isolation.
|
|
|
|
|
|
# 1. The `OverloadedRecordFields` extension
|
|
|
|
|
|
|
... | ... | @@ -187,69 +190,34 @@ We could mangle selector names (using `$sel:foo:T` instead of `foo`) even when t |
|
|
|
|
|
## 2. The `ImplicitValues` extension
|
|
|
|
|
|
TODO
|
|
|
|
|
|
## 3. The magic type classes
|
|
|
|
|
|
|
|
|
The `HasField` and `FieldUpdate` classes, and `FieldType` and `UpdatedRecordType` type families, will be defined in the module `GHC.Records` in the `base` package.
|
|
|
|
|
|
```wiki
|
|
|
$dfHasTx :: forall a . a ~ Int => Has T "x" b -- corresponds to the Has instance decl
|
|
|
$dfHasTx = Has { getField _ = $sel_x_T }
|
|
|
|
|
|
$dfUpdTx :: forall a . a ~ Int => Upd T "x" a -- corresponds to the Upd instance decl
|
|
|
$dfUpdTx = Upd { setField _ s e = s { x = e } }
|
|
|
|
|
|
axiom TFCo:R:FldTy:T:x : FldTy T "x" = Int -- corresponds to the FldTy type family instance
|
|
|
axiom TFCo:R:UpdTy:T:x : UpdTy T "x" Int = T -- corresponds to the UpdTy type family instance
|
|
|
```
|
|
|
|
|
|
## Automatic instance generation
|
|
|
|
|
|
|
|
|
Typeclass and family instances are generated and typechecked by `makeOverloadedRecFldInsts` in `TcInstDecls`, regardless of whether or not the extension is enabled. This is called by `tcTopSrcDecls` to generate instances for fields from datatypes in the current group (just after derived instances, from **deriving** clauses, are generated). Overloaded record field instances are not exported to other modules (via `tcg_insts` and `tcg_fam_insts`), though underlying dfun ids and axioms are exported from the module as usual (via `tcg_binds` and a new field `tcg_axioms`). The new field is needed because there is otherwise no way to export an axiom without exporting the corresponding family instance.
|
|
|
This part is new in the 2015 redesign, and has not previously been implemented, so there is not yet much to record here. However, the implementation should be fairly straightforward and close to (but simpler than) the existing `ImplicitParameters` extension.
|
|
|
|
|
|
## 3. The magic type classes
|
|
|
|
|
|
Since the instances are not in scope in the usual way, `matchClassInst` and `tcLookupFamInst` look for the relevant constraints or type families and find the instances directly, rather than consulting `tcg_inst_env` or `tcg_fam_inst_env`. They first perform a lookup to check that the field name is in scope.
|
|
|
|
|
|
## Unused imports
|
|
|
The `HasField` and `FieldUpdate` classes, and `FieldType` and `UpdatedRecordType` type families, will be defined in the module `GHC.Records` in the `base` package. Contrary to the previous design, we will not generate any dfuns/axioms for these classes \*at all\*. Instead, the typechecker will implicitly create evidence as required. This gets rid of a whole lot of complexity.
|
|
|
|
|
|
|
|
|
Unused imports and generation of the minimal import list (`RnNames.warnUnusedImportDecls`) use a map from selector names to labels, in order to print fields correctly. Moreover, consider the following:
|
|
|
The only additional things that need to be generated at datatype declarations are updater functions (one per field), which correspond to the selector functions that are already generated. So for example
|
|
|
|
|
|
```wiki
|
|
|
module A where
|
|
|
data T = MkT { x,y:Int }
|
|
|
|
|
|
module B where
|
|
|
data S = MkS { x,y::Bool }
|
|
|
|
|
|
module C where
|
|
|
import A( T(x) )
|
|
|
import B( S(x) )
|
|
|
|
|
|
foo :: T -> Int
|
|
|
foo r = x r + 2
|
|
|
data T = MkT { x, y :: Int }
|
|
|
```
|
|
|
|
|
|
|
|
|
Now, do we expect to report the `import B( S(x) )` as unused? Only the typechecker will eventually know that. To record this, I've added a new field `tcg_used_selectors :: TcRef NameSet` to the `TcGblEnv`, which records the selector names for fields that are encountered during typechecking (when looking up a `Has` instance etc.). This set is used to calculate the import usage and unused top-level bindings. Thus a field will be counted as used if it is needed by the typechecker, regardless of whether any definitions it appears in are themselves used.
|
|
|
|
|
|
|
|
|
Unused local bindings are trickier, as the following example illustrates:
|
|
|
will generate
|
|
|
|
|
|
```wiki
|
|
|
module M (f)
|
|
|
data S = MkS { foo :: Int }
|
|
|
data T = MkT { foo :: Int }
|
|
|
$sel:x:T :: T -> Int
|
|
|
$sel:x:T (MkT x _) = x
|
|
|
|
|
|
f = foo (MkS 3)
|
|
|
g x = foo x
|
|
|
$upd:x:T :: T -> Int -> T
|
|
|
$upd:x:T (MkT _ y) x = MkT x y
|
|
|
```
|
|
|
|
|
|
|
|
|
The renamer calculates the free variables of each definition, to produce a list of `DefUses`. In both `f` and `g` we get potential uses of `S(foo)` and `T(foo)`, but the typechecker will discover that `f` uses only `S(foo)` while `g` uses neither. (But `g` requires `foo` to be in scope somehow!) The simplest thing is to make an occurrence of an overloaded field in an expression return as free variables all the selectors it might refer to. This will sometimes fail to report unused local bindings: in the example, it will not spot that `T(foo)` is unused.
|
|
|
The updater function will always have a name prefixed with `$upd:`, regardless of whether `OverloadedRecordFields` is enabled.
|
|
|
|
|
|
## GADT record updates
|
|
|
|
... | ... | @@ -265,15 +233,15 @@ data W a where |
|
|
It would be nice to generate
|
|
|
|
|
|
```wiki
|
|
|
-- setField :: Proxy# "x" -> W (a, b) -> a -> W (a, b)
|
|
|
setField _ s e = s { x = e }
|
|
|
-- $upd:x:W :: W (a, b) -> a -> W (a, b)
|
|
|
$upd:x:W s e = s { x = e }
|
|
|
```
|
|
|
|
|
|
|
|
|
but this record update is rejected by the typechecker, even though it is perfectly sensible, because of [\#2595](https://gitlab.haskell.org//ghc/ghc/issues/2595). The currently implemented workaround is instead to generate the explicit update
|
|
|
|
|
|
```wiki
|
|
|
setField _ (MkW _ y) x = MkW x y
|
|
|
$upd:x:W (MkW _ y) x = MkW x y
|
|
|
```
|
|
|
|
|
|
|
... | ... | @@ -282,36 +250,40 @@ which is fine, but rather long-winded if there are many constructors or fields. |
|
|
|
|
|
Note that `W` does not admit type-changing single update for either field, because of the `a ~ b` constraint. Without it, though, type-changing update should be allowed.
|
|
|
|
|
|
# Current status
|
|
|
|
|
|
## Unused imports
|
|
|
|
|
|
We stalled previously on a refactoring to make `FieldLabel`s (stored in `TyCon`s) contain actual `CoAxiom`s, not just their `Name`s. This would allow them to be `implicitTyThings` of the `TyCon`, rather than requiring them to be brought into scope separately. (At the moment, there are new `tcg_axioms`, `mg_axioms` and `ic_axioms` fields to pass around axioms without corresponding type family instances.)
|
|
|
|
|
|
Unused imports and generation of the minimal import list (`RnNames.warnUnusedImportDecls`) use a map from selector names to labels, in order to print fields correctly. Moreover, consider the following:
|
|
|
|
|
|
However, at the moment construction of the axioms is intertwined with construction of the class instances, and the type of an axiom depends in fairly subtle ways on the structure of the `TyCon` (e.g. the types of all its fields). Thus attempts to build the axioms when creating the `TyCon` tend to end up in infinite loops caused by poking the wrong fields of the `TyCon` too early. (See also `"Note [Tricky iface loop]"` in LoadIface.)
|
|
|
```wiki
|
|
|
module A where
|
|
|
data T = MkT { x,y:Int }
|
|
|
|
|
|
## Other outstanding issues
|
|
|
module B where
|
|
|
data S = MkS { x,y::Bool }
|
|
|
|
|
|
module C where
|
|
|
import A( T(x) )
|
|
|
import B( S(x) )
|
|
|
|
|
|
From the latest merge with HEAD:
|
|
|
foo :: T -> Int
|
|
|
foo r = #x r + 2
|
|
|
```
|
|
|
|
|
|
- A few failing test cases remain.
|
|
|
- The AST still contains landmines that should be defused.
|
|
|
- The Haddock changes need testing.
|
|
|
|
|
|
Now, do we expect to report the `import B( S(x) )` as unused? Only the typechecker will eventually know that. To record this, a new field `tcg_used_selectors :: TcRef NameSet` in the `TcGblEnv` records the selector names for fields that are encountered during typechecking (when looking up a `HasField` instance etc.). This set is used to calculate the import usage and unused top-level bindings. Thus a field will be counted as used if it is needed by the typechecker, regardless of whether any definitions it appears in are themselves used.
|
|
|
|
|
|
Old notes on possible refactoring:
|
|
|
|
|
|
- The definition of `tcFldInsts` has a slightly fragile assertion that it does not obtain any evidence bindings when typechecking `Has`/`Upd` instances. Could these be returned somewhere instead? It should be possible to fuse `makeRecFldInstsFor` and `tcFldInsts`, or just generate and typecheck binds, using `tcValBinds` or similar for the typechecking.
|
|
|
- Perhaps we should make record selector bindings in `TcFldInsts`, along with the new code.
|
|
|
- We shouldn't need to mess with the `TypeEnv` in `tcRnHsBootDecls`. Instead:
|
|
|
Unused local bindings are trickier, as the following example illustrates:
|
|
|
|
|
|
1. `tcTyClsInstDecls` should populate it with the `dfun_ids`
|
|
|
1. `tcHsBootSigs` should populate it with `val_ids` and return an updated `TcGblEnv`
|
|
|
1. Add a new field `tcg_boot_ids :: Bag Id` to `TcGblEnv` and pass `val_ids` to `mkBootModDetailsTc` that way, so it doesn't need to use the `TypeEnv`
|
|
|
```wiki
|
|
|
module M (f)
|
|
|
data S = MkS { foo :: Int }
|
|
|
data T = MkT { foo :: Int }
|
|
|
|
|
|
f = #foo (MkS 3)
|
|
|
g x = #foo x
|
|
|
```
|
|
|
|
|
|
Future considerations:
|
|
|
|
|
|
- Consider defaulting `Accessor p r n` to `p = (->)`, and defaulting `Has r "x"` constraints where there is only one datatype with a field `x` in scope.
|
|
|
- Add syntax for record projection, perhaps using \# since it shouldn't conflict with `MagicHash`? |
|
|
The renamer calculates the free variables of each definition, to produce a list of `DefUses`. The typechecker will discover that `f` uses only `S(foo)` while `g` uses neither. The simplest thing is to make an occurrence of an overloaded field in an expression return as free variables all the selectors it might refer to. This will sometimes fail to report unused local bindings: in the example, it will not spot that `T(foo)` is unused. |