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

Evan Laforge qdunkan at gmail.com
Fri Feb 10 19:11:02 CET 2012


On Thu, Feb 9, 2012 at 10:03 PM, Donn Cave <donn at avvanta.com> wrote:
> Quoth Evan Laforge <qdunkan at gmail.com>,
>> On Thu, Feb 9, 2012 at 12:49 PM, Donn Cave <donn at avvanta.com> wrote:
> ...
>>> For example, in a better world you could write stuff like
>>>
>>>   modifyConfig :: (Config -> a) -> (a -> a) -> Config -> Config
>>>   modifyConfig fr fv a = a { fr = fv (fr a) }
>>>
>>>   upTempo config = modifyConfig tempo (+ 20) config
>>
>> I think lenses already do better than this, since not only are they
>> more concise than the above (once you've resigned yourself to a few TH
>> splices), they aren't restricted to being only record fields.
>
> How more concise?  Because =# is more concise than `modifyRecord', etc.,
> or is there some real economy of expression I missed out on?  Asking
> because, honestly I didn't get your earlier example -

More concise because in your example (which is also what most of my
code looks like), you define a modifyX function and then apply it to
form the setField function.  To be complete you would have to define a
modifyX for every branch in the nested records.  It's rare for records
go above 3 levels deep, but you can still wind up with quite a
function boilerplate style modifyX functions.

In the case of lenses, all the relevant modifyX functions are
generated automatically and can be composed.

> setTempo :: Config -> Config
> setTempo y = Config.deflt#Config.tempo =# y
>
> ... something's missing, I thought - but maybe it's conciser than
> I can reckon with!

Getting rid of the special operators and eta reduction might make it clearer:

setTempo y config = set (Config.tempo `composeLens` Config.deflt) y config

> The rest - the functions that look like fields, the enforcing invariants,
> etc. - are cool as lens features, but for Haskell records in general it
> seems like something that would call for a lot of discussion.  Compared
> to first class record update, where it's easy to see how close to broken
> the current situation is.

Well, that's why I'm saying we don't have to build the lens features
into the language, though I think at some point one of those lens
libraries should make it into the platform and be encouraged as the
standard way.  I think the field access / modification problem has
already been solved, and I can't even think of a better way to do it.
You could build them into the language by having the record
declaration syntax automatically create lenses instead of plain access
functions.  But that would make it harder to swap out the
implementation, and I don't know if there's sufficient confidence in
the implementations that people are ready to commit to one and build
it into the compiler.  It depends how much people hate the TH gunk
implied by not having the derivation built in.

I think a reasonable course is to use the TH gunk for now and if the
world coalesces on one implementation or if everyone loves the new
records and wants to enshrine them in haskell' then it gets built in.
TH is good as a trying ground for new features.

The thing I think *is* "broken" (well, just awkward, really), is that
I have to type 'set (Config.tempo . Config.deflt)' instead of 'set
(tempo.deflt)'.  Once we get there, then (back to my wacky operators)
'deflt#tempo =# 42 config' is just a jumbled version of the imperative
'config.tempo := 42' only better because it can be partially applied.
Then we just add a lens for Data.Map and imperative
'state[block].config.tempo := 42' can be written 'Map.lens block #
config # tempo #= 42 config'... not bad!  To be sure the only
difference with the current situation is that you have to qualify
those names.



More information about the Haskell-Cafe mailing list