... | ... | @@ -67,25 +67,35 @@ The `r{ ... }` is syntactic sugar for the constraint meaning "record `r` has fie |
|
|
|
|
|
We need a way to declare that a name is available as an overloadable field name (roughly speaking, a class/method definition), proposed syntax:
|
|
|
|
|
|
**Option One: new `fieldLabel` style of declaration:**
|
|
|
|
|
|
```wiki
|
|
|
fieldLabel customer_id :: r -> Int
|
|
|
```
|
|
|
|
|
|
>
|
|
|
> (The `r{ ... }` is added by the desugarer.)
|
|
|
|
|
|
**Option Two: explicit record constraint on the function:**
|
|
|
|
|
|
```wiki
|
|
|
customer_id :: r{ customer_id :: Int} => r -> Int -- field name same as the declared function
|
|
|
```
|
|
|
|
|
|
(The `r{ ... }` is added by the desugarer.)
|
|
|
>
|
|
|
> (See discussion at [Wild afterthought](records/declared-overloaded-record-fields/c-ompare-sorf#the-string-type-parameter-to-has-,-and-scope-control).)
|
|
|
|
|
|
|
|
|
The `-> Int` means the field's domain (type) is `Int` -- it's just a type.
|
|
|
We might also want to constrain the record -- for example to be sure it is savable to persistent storage:
|
|
|
|
|
|
```wiki
|
|
|
fieldLabel unitPrice :: (Save r, Num t) => r -> t
|
|
|
fieldLabel unitPrice :: (Save r, Num t) => r -> t -- again the `r{ ... }` gets added as a further constraint
|
|
|
-- or
|
|
|
unitPrice :: (r{ unitPrice :: t}, Save r, Num t) => r -> t -- again repeated field name
|
|
|
```
|
|
|
|
|
|
|
|
|
(Again the `r{ ... }` gets added as a further constraint.)
|
|
|
|
|
|
|
|
|
Now we can use the field in a record, and that in effect declares an instance for the field/record. All these definitions are in the same module:
|
|
|
|
|
|
```wiki
|
... | ... | @@ -98,8 +108,10 @@ Now we can use the field in a record, and that in effect declares an instance fo |
|
|
data Customer_Order = Cust_Order { customer_id :: Int, ... }
|
|
|
```
|
|
|
|
|
|
### Field Selection
|
|
|
|
|
|
|
|
|
Then a field selection expression like:
|
|
|
With those records declared, a field selection expression like:
|
|
|
|
|
|
> `... (customer_id r) ...` -- H98 style field application
|
|
|
|
... | ... | @@ -112,7 +124,7 @@ uses familiar type instance resolution to figure out from record type `r` how to |
|
|
> `... r.customer_id ...`
|
|
|
|
|
|
|
|
|
See \<Dot as Postfix Func Apply\> for that dot notation, but note that nothing in this proposal assumes dot notation will be needed.\]
|
|
|
See [Dot as Postfix Function Apply](records/declared-overloaded-record-fields/dot-postfix) for that dot notation, but note that nothing in this proposal assumes dot notation will be needed.\]
|
|
|
|
|
|
|
|
|
From here upwards, the `r{ ... }` constraint is just a constraint, and gets merged with other constraints. For example, you could define a function:
|
... | ... | @@ -125,7 +137,9 @@ From here upwards, the `r{ ... }` constraint is just a constraint, and gets merg |
|
|
The type inferred would be:
|
|
|
|
|
|
```wiki
|
|
|
fullName :: r{ firstName, lastName :: String} => r -> String
|
|
|
fullName :: r{ firstName, lastName :: String} => r -> String -- could declare this for yourself
|
|
|
-- note this is __not__ like a field label decl (Option Two)
|
|
|
-- because the function's name is different to the field(s)
|
|
|
```
|
|
|
|
|
|
|
... | ... | @@ -137,7 +151,7 @@ which is eliding: |
|
|
```
|
|
|
|
|
|
|
|
|
And if you think that's very close to the type of a field selector function, you'd be right. Here's some more examples of pseudo- or 'virtual' fields, using dot notation:
|
|
|
And if you think that's very close to the type of a field selector function, you'd be right. Here's some more examples of field selection using **pseudo-** or** 'virtual' **fields, with dot notation:
|
|
|
|
|
|
```wiki
|
|
|
customer.fullName
|
... | ... | @@ -223,7 +237,7 @@ You can (continue to) use pattern matching and data constructor tagging for reco |
|
|
|
|
|
```wiki
|
|
|
case r of {
|
|
|
Cust_Price {unit_Price, ..} -> Cust_Price {unit_Price = unit_Price * 1.05, .. }
|
|
|
Cust_Price{ unit_Price, .. } -> Cust_Price{ unit_Price = unit_Price * 1.05, .. }
|
|
|
} -- increases Price by 5%
|
|
|
```
|
|
|
|
... | ... | @@ -241,7 +255,7 @@ The new part is polymorphic record update: |
|
|
Returns a record with same fields as `myPrice`, except a different `unit_Price`. Note that the update can change the type of a field (if the record declaration is polymorphic).
|
|
|
|
|
|
|
|
|
Note that upon first encountering that expression, we don't know the record types (because `unit_Price` is overloaded). So the types initially inferred are:
|
|
|
Upon first encountering that expression, we don't know the record types (because `unit_Price` is overloaded). So the types initially inferred are:
|
|
|
|
|
|
```wiki
|
|
|
<expr> :: r { unit_Price :: Int } => r
|
... | ... | @@ -252,7 +266,7 @@ Note that upon first encountering that expression, we don't know the record type |
|
|
That is, the update might be changing the record type as well as the field type -- in case that the record type is parametric over the field type.
|
|
|
|
|
|
|
|
|
Behind the scenes, the update syntax with an expression prefix to the `{ ... }` is syntactic sugar for a call to the polymorphic record update method `set`:
|
|
|
Behind the scenes, the update syntax with an expression prefix `e{ ... }` (as opposed to a data constructor `MkT{ .. }`) is syntactic sugar for a call to the polymorphic record update method `set`:
|
|
|
|
|
|
```wiki
|
|
|
set (undefined :: Proxy_unit_Price) (72 :: Int) myPrice
|
... | ... | @@ -268,7 +282,7 @@ Normal type inference/instance resolution will find the record type for `myPrice |
|
|
You can update multiple fields at the same time:
|
|
|
|
|
|
```wiki
|
|
|
myCustNA { firstName = "Fred", lastName = "Dagg" }
|
|
|
myCustNA{ firstName = "Fred", lastName = "Dagg" }
|
|
|
```
|
|
|
|
|
|
>
|
... | ... | |