Syntax for modifying nested product types

I have data objects where each component is a labelled field through which I access or modify it. I have a hierarchy of these - many of the fields are themselves such data objects, so I may need to apply a few selector functions to get down to what I want (call these "deep"). For fairly flat things, the updates using field labels are okay. However, when I have a function that needs to tweak various bits of various "deep" fields, based on various conditions, then it gets really ugly, not least because of the conditions: foo { bar = baz } becomes, let old = bar foo in foo { bar = if cond then f old else old } or whatever. Do I need such a let for each component and sub-component of the object that has a child that may be modified, or will foo { bar = if cond then f (bar foo) else bar foo } be suitably optimised? (The if's inside the {} because there may be multiple fields (or children thereof) being modified based on different conditions.) -- Mark

Mark Carroll writes:
For fairly flat things, the updates using field labels are okay. However, when I have a function that needs to tweak various bits of various "deep" fields, based on various conditions, then it gets really ugly, not least because of the conditions: foo { bar = baz } becomes,
let old = bar foo in foo { bar = if cond then f old else old }
or whatever. Do I need such a let for each component and sub-component of the object that has a child that may be modified, or will
foo { bar = if cond then f (bar foo) else bar foo }
be suitably optimised?
If the pattern is very common, how about just naming it? perhaps cond f field = if cond then f field else field foo { bar = perhaps cond0 f (bar foo) , wib = perhaps cond1 g (wib foo) } Regards, Malcolm

On Fri, Apr 23, 2004 at 10:11:40AM +0100, Malcolm Wallace wrote:
If the pattern is very common, how about just naming it?
perhaps cond f field = if cond then f field else field
foo { bar = perhaps cond0 f (bar foo) , wib = perhaps cond1 g (wib foo) }
Good idea, but I think that name like 'applyIf' or 'condApply' would be better. Best regards, Tom -- .signature: Too many levels of symbolic links

Malcolm Wallace wrote:
perhaps cond f field = if cond then f field else field
foo { bar = perhaps cond0 f (bar foo) , wib = perhaps cond1 g (wib foo) }
I tend to write functions: upd_bar f x = x { bar = f (bar x) } upd_wib f x = x { wib = f (wib x) } in order to avoid mentioning the field names twice later on. Does it cost much if I sometimes supply "id" as update argument? It would be nice if such update functions could be generated or if there were another special update syntax. Does it exist in other languages? Cheers Christian

At 18:18 22/04/04 -0400, Mark Carroll wrote:
I have data objects where each component is a labelled field through which I access or modify it.
Wading into the labelled field debate... I have found that using the labelled field update syntax can lead to difficulties in changing the underlying implementation of a type. I found this in particular when re-implementing the Network.URI module: I found that I needed to change the internal representation of the URI type in order to satisfy the recently clarified URI specification. The knock-on effect of this was mitigated to some extent by implementing the original field names as functions to extract values corresponding to the original components. But I found when porting the HXml Toolbox and/or HTTP code to use this new implementation that while field references would still work as intended, occurrences of the field update syntax had to be re-coded; there remained external dependencies on the internal structure of URI that could not be "shimmed" away with new functions. This leads me to ask, if new syntax approaches for labelled fields are being considered, if it would be possible to adopt an approach in which the field name works like a function for both access *and* updating of a field record. A clumsy example of this might be a field name used as a query-and-set function: data record { field :: atype, ... } yields field :: record -> (atype -> atype) -> (record,atype) then arecord :: record arecord = ... fval = snd field arecord id -- returns value of field rval = fst field arecord (const newval) -- returns updated record (this is to illustrate a desideratum, not a serious suggestion. Something is tickling the back of my mind about possibly doing this with a Monad.) With a field name working as a function (the same function) for both access and update, it is then possible to change the internal structure of a record yet retain the possibility of presenting a backwards-compatible interface. #g ------------ Graham Klyne For email: http://www.ninebynine.org/#Contact

Graham Klyne
At 18:18 22/04/04 -0400, Mark Carroll wrote:
I have data objects where each component is a labelled field through which I access or modify it.
Wading into the labelled field debate...
I have found that using the labelled field update syntax can lead to difficulties in changing the underlying implementation of a type.
Yeah, interesting this topic came up exactly when I got mad with record updates. I'd like to fill in a record from a file which contains labels and values. I'd like to give the gist of this operation as a list of pairs: [("label1", label1), ("label2", label2), ...] and abstract away the operation of updating the field. I couldn't find a way. Basically, I couldn't write an update function parametrized by the field name. Is it possible at all? -- Feri.
participants (6)
-
Christian Maeder
-
Ferenc Wagner
-
Graham Klyne
-
Malcolm Wallace
-
Mark Carroll
-
Tomasz Zielonka