
Donn Cave
-- modifyRecord :: RecordType r => (a -> a) -> (r -> a) -> r -> r
modifyRecord :: RecordType r => (r -> a) -> (a -> a) -> r -> r
... while this does obviously represent a polymorphic function,
Exactly!
if I write
-- data Config { tempo :: Int, ...} data Config = Config { tempo :: Int, ...} f = modifyRecord tempo (+20) ...
But f defined like that is exactly what you can't write now (even with the args round the same way as the signature ;-), because: * `tempo' is a function to select a field out of a record, *and only that*. So there's no way in the body of modifyRecord to use its (r -> a) argument to put the updated `a' back into `r'. * You can't (in current Haskell) put in place of `tempo' any type/species of a term that could achieve that update, except by either: making modifyRecord in effect monomorphic to Config/tempo, or building a polymorphic update system wot we 'ave no' go' (yet).
... then f has type Config -> Config, it isn't polymorphic. You can do: f Config{ tempo, .. } = Config {tempo = tempo + 20, ..} And that does yield f :: Config -> Config
(But I'm sure you knew that.) OK, we could implement lenses, make `tempo' a lens instead of a selector, desugar the update syntax to call the update 'method' out of the lens, ... And of course somehow arrange the sugar that when `tempo' appears in other contexts we take the select 'method'. You write up the proposal, and think through all the changes it would involve over Haskell/GHC as is, and then we can compare it to all those other proposals. I think you'll still find you run into exactly the same difficulties I mentioned around update for record changing, Higher-ranked, etc.
I am however vaguely aware that some parties to the Record Question would like to make record fields themselves polymorphic,
Yes, for example Jonathan Geddes' post:
setName n r = r {name = n} addMr r = r { name = "Mr. " ++ (name r) }
(Jonathan's post is asking for alternative syntax: that's rather ambitious when we can't yet write anything like that currently, indeed we don't even know how we could implement it in general.) His context is, presumably, having lots of different record types all with a field `name'. (Arguably he should adopt long_and_meaningful_names for his various fields.)
Maybe that's semantically more like "overloading",
Yes, I've implemented it as overloading.
but in any case, it isn't strictly necessary in order to support first class updates, true?
Donn
Well, I think we might be getting stuck here with what does 'first class update' mean? The narrow issue we're trying to address is namespacing, and specifically name clashes: two different records with the same named field. I can't do better than quote SPJ again, sorry (not very) to repeat myself:
SPJ in the SORF proposal asks: what does e { x = True } mean if there are lots of "x" fields in scope? (which is precisely what we want to allow)
It's true that each "x" is monomorphic (in the sense of being tied to a specific record and field type), but at the time the compiler encounters that expression, it doesn't know the type of `e'. (In general, `e' is some arbitrary expression -- perhaps selecting a record out of a keyed array?) So the compiler relies on the name "x" being monomorphic to tell it. In contrast, -XDisambiguateRecordFields copes with different "x"s by insisting you put the Record's data constructor in place of the expression `e'. If we want to turn this into a syntax question, we perhaps need a way of putting both an expression and a data constructor in with the field and the value to update. But note that the "x" in { x = True } is sort of hard-coded, there's currently no way to put an expression in its place. So you still can't define a modifyConfig: you couldn't put anything in place of its (r -> a) parameter that could represent "x". Now in return for me answering that, please answer the questions in my earlier post about what limitations on update you'd like: * record-type changing? * Higher-ranked fields? * How many forall'd variables? * Constrained forall'd variables? Thank you AntC