[Haskell-cafe] Some thoughts on Type-Directed Name Resolution -record update

AntC anthony_clayden at clear.net.nz
Fri Feb 10 05:33:41 CET 2012


Donn Cave <donn <at> avvanta.com> writes:

> 
> 
> --  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




More information about the Haskell-Cafe mailing list