record-dot-preprocessor icon indicating copy to clipboard operation
record-dot-preprocessor copied to clipboard

Compatibility with lens

Open seanhess opened this issue 5 years ago • 3 comments

Hi there, thank you so much for making this. I can't wait for the extension, so I am enjoying this.

One of the features of RecordDotSyntax is it's lens compatibility. According to posters on the internet, it's supposed to be straightforward to declare both a record using dot syntax, and field-based lenses when you need them.

See here. The commenter says you can use the same name for the lens as the field, since they inhabit difference namespaces. This does not seem to be true in my tests. Also see here

Am I missing something? How do you recommend using this preprocessor if you want to also use lenses?

Thanks again!

seanhess avatar Nov 10 '20 03:11 seanhess

I think there are two things:

  1. You can define lenses entirely separately to this mechanism and continue using them. What the commentator is saying is that the lens names don't clash, because they are different. So use lens as normal. And if you want, use this extension. If you try it and it gives an error, I'd be keen to see the code + error.
  2. You can define fieldLens that generates a lens given a field name. E.g.
  mkLens :: forall x r a . HasField x r a => Lens' r a
  mkLens f r = wrap <$> f v
      where (wrap, v) = hasField @x r

While we know ways you can use them together, until someone tries for real, we don't have any best practices or similar.

ndmitchell avatar Nov 10 '20 09:11 ndmitchell

I'm running into this problem again, and I'd like to come up with an example. The goal is to define a record using this preprocessor without any prefixes and create lenses matching the field names. We want something like this to work:

{-# OPTIONS_GHC -F -pgmF=record-dot-preprocessor #-}
import Control.Lens

data Person = Person { name :: String, age :: Int } 
makeLenses ''Person

bob = Person "Bob" 33

main = do
  print bob.name
  print (bob ^. name) -- error! No lenses are generated in this example. 
  1. In the above example, field selectors for name and age are generated, which would conflict with any lenses of the same name. There's a NoFieldSelectors extension, but it isn't available in my recent test using GHC 8.10
  2. No lenses are generated in the above example, because the default rules expect an underscore prefix, and ignore any fields without one.

So, my question is, should this preprocessor implement the effects of NoFieldSelectors? Or is there a workaround to do this so that lenses of the same name can be generated?

I'll go figure out how to get lens to generate lenses exactly matching the field names, unless you know off the top of your head.

seanhess avatar Jul 02 '21 18:07 seanhess

This preprocessor doesn't intend to implement the effects of NoFieldSelectors - it's just a different problem and I don't know how a preprocessor would manage it. I think if you enable some of the disambiguate record field options it might work? As the first step, I'd try and get it working without the record preprocessor, and without the bob.name. Once you can get it working without, then it might be the case that the preprocessor is incompatible with that solution, which might be worth solving.

ndmitchell avatar Jul 03 '21 14:07 ndmitchell