| ... | ... | @@ -138,6 +138,134 @@ It is often useful to limit the ability of users to fill in or access parts of a |
|
|
|
This would insure that by not exporting a field label, it cannot be gotten around by using positional notation.
|
|
|
|
This fix would also require the polymorphic setting ability mentioned above and would partially mitigate the need for [ReadonlyConstructors](readonly-constructors)
|
|
|
|
|
|
|
|
## Polymorphic Record Update take II
|
|
|
|
|
|
|
|
|
|
|
|
(The following was discussed briefly on the Haskell' list.)
|
|
|
|
Consider the following data type:
|
|
|
|
|
|
|
|
```wiki
|
|
|
|
data T a
|
|
|
|
= C1 { f1 :: a }
|
|
|
|
| C2 { f1 :: a, f2 :: Int }
|
|
|
|
| C3 { f2 :: Int }
|
|
|
|
deriving Show
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Suppose we want to update the field `f1` only in such a way that
|
|
|
|
its type changes. We cannot use the record update syntax, as not
|
|
|
|
all constructors have a field `f1`. So we write a utility function.
|
|
|
|
However, we would prefer to do as little as possible when it
|
|
|
|
comes to values constructed by constructors NOT having a field
|
|
|
|
`f2`. One might naively try this:
|
|
|
|
|
|
|
|
```wiki
|
|
|
|
foo :: T a -> T Int
|
|
|
|
foo x@(C1 {}) = x {f1 = 1}
|
|
|
|
foo x@(C2 {}) = x {f1 = 2}
|
|
|
|
foo x = x
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
But of course, this does not type check as the type of `x` is
|
|
|
|
different on the LHS and RHS. We can get around that by reconstructing
|
|
|
|
the value on the RHS:
|
|
|
|
|
|
|
|
```wiki
|
|
|
|
foo :: T a -> T Int
|
|
|
|
foo x@(C1 {}) = x {f1 = 1}
|
|
|
|
foo x@(C2 {}) = x {f1 = 2}
|
|
|
|
foo x@(C3 {f2 = n}) = C3 {f2 = n}
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
However, this is bad, because we have to change the code if further
|
|
|
|
constructors are added, even when they do not have a field `f1`,
|
|
|
|
and we also have to change the code if further fields are added
|
|
|
|
to constructors not having the field `f1`. This is tedious,
|
|
|
|
error prone, and really defeats one of the main reasons for using
|
|
|
|
records in the first place. For example:
|
|
|
|
|
|
|
|
```wiki
|
|
|
|
data T a
|
|
|
|
= C1 { f1 :: a }
|
|
|
|
| C2 { f1 :: a, f2 :: Int }
|
|
|
|
| C3 { f2 :: Int, f3 :: Char }
|
|
|
|
| C4 { f2 :: Int }
|
|
|
|
deriving Show
|
|
|
|
|
|
|
|
foo :: T a -> T Int
|
|
|
|
foo x@(C1 {}) = x {f1 = 1}
|
|
|
|
foo x@(C2 {}) = x {f1 = 2}
|
|
|
|
foo x@(C3 {f2 = n, f3 = c}) = C3 {f2 = n, f3 = c}
|
|
|
|
foo x@(C4 {f2 = n}) = C4 {f2 = n}
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
One might think it would be possible to do better if we're furtunate
|
|
|
|
enough to have a field that is common to \*all\* constructors not having
|
|
|
|
a field `f1`, as is the case for `f2` in this case:
|
|
|
|
|
|
|
|
```wiki
|
|
|
|
foo :: T a -> T Int
|
|
|
|
foo x@(C1 {}) = x {f1 = 1}
|
|
|
|
foo x@(C2 {}) = x {f1 = 2}
|
|
|
|
foo x = x {f2 = f2 x}
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
But this does not type check, and it would not apply anyway if
|
|
|
|
there is no such common field.
|
|
|
|
|
|
|
|
|
|
|
|
What we really need is a function that reconstructs a value of type `T a`
|
|
|
|
at type `T b` for all values constructed by a constructor that does not have
|
|
|
|
a field `f1`:
|
|
|
|
|
|
|
|
```wiki
|
|
|
|
coerce_no_f1 :: T a -> T b
|
|
|
|
coerce_no_f1 x@(C3 {f2 = n, f3 = c}) = C3 {f2 = n, f3 = c}
|
|
|
|
coerce_no_f1 x@(C4 {f2 = n}) = C4 {f2 = n}
|
|
|
|
|
|
|
|
foo :: T a -> T Int
|
|
|
|
foo x@(C1 {}) = x {f1 = 1}
|
|
|
|
foo x@(C2 {}) = x {f1 = 2}
|
|
|
|
foo x = coerce_no_f1 x
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
But we'd rather not have to write such functions by hand, just as
|
|
|
|
we'd rather not write update functions by hand. Maybe the record
|
|
|
|
update syntax could be extended so that the function that gets
|
|
|
|
generated behind the scenes only includes constructors that
|
|
|
|
does NOT mention a particular field. For example, the field
|
|
|
|
name(s) that must not occur could be prefixed by `~` which suggests
|
|
|
|
negation in some settings. It does not have this connotation in Haskell,
|
|
|
|
but at least `~` is already a special symbol. We could then write:
|
|
|
|
|
|
|
|
```wiki
|
|
|
|
foo :: T a -> T Int
|
|
|
|
foo x@(C1 {}) = x {f1 = 1}
|
|
|
|
foo x@(C2 {}) = x {f1 = 2}
|
|
|
|
foo x = x {~f1}
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
Now the code for `foo` only has to be changed if new constructors
|
|
|
|
having a field `f1` are added.
|
|
|
|
|
|
|
|
|
|
|
|
Of course, it should be possible to combine this with the normal
|
|
|
|
record update syntax. E.g.
|
|
|
|
|
|
|
|
```wiki
|
|
|
|
foo :: T a -> T Int
|
|
|
|
foo x@(C1 {}) = x {f1 = 1}
|
|
|
|
foo x@(C2 {}) = x {f1 = 2}
|
|
|
|
foo x = x {~f1, f2 = f2 x + 1}
|
|
|
|
```
|
|
|
|
|
|
|
|
# Meta-Proposal
|
|
|
|
|
|
|
|
|
| ... | ... | |