Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Generating lenses for a "lens" library with a custom name processor instead of the default "underscore"-based one

The standard makeLenses implementation generates lenses for all the fields of a record which begin with underscore. I very much dislike the idea of having to introduce such an awkward naming convention to my records for many reasons. What I want to do is just generate lenses for all fields of a record and name them by just appending a suffix "L" to field names.

With an fc-labels library all I had to do to achieve that was

mkLabelsWith (++ "L") [''MyRecord]

but the lens library has a much more involved configuration with rulesets and stuff, which isn't as easy to get a mind around. So I'm asking for a specific recipe to just achieve the same thing.

like image 280
Nikita Volkov Avatar asked Jun 16 '13 10:06

Nikita Volkov


2 Answers

shachaf's answer doesn't work with lenses 4.4+ which changed the template-haskell related APIs.

Here is the implementation that works with those versions:

import Language.Haskell.TH

myMakeLenses :: Name -> DecsQ
myMakeLenses = makeLensesWith $ lensRules
    & lensField .~ \_ _ name -> [TopName (mkName $ nameBase name ++ "L")]

(this is 4.5+, in 4.4 the lambda would take only \_ name as parameters)

like image 76
Emmanuel Touzery Avatar answered Nov 10 '22 11:11

Emmanuel Touzery


Looking at the code, it seems pretty straightforward. LensRules have a function lensField :: String -> Maybe String (which either gives the name for a lens or fails). So you can make a function like

myMakeLenses = makeLensesWith $ lensRules
  & lensField .~ (\name -> Just (name ++ "L"))

and use that instead of makeLenses. Of course you could parameterize your function on (++ "L").

Or you could write it inline if you prefer, e.g.

makeLensesWith ?? ''Foo $ lensRules
  & lensField .~ (\name -> Just (name ++ "L"))

(Note that (??) is just infix flip for passing the arguments in the right order. You can think of it as a "hole" in this case that the second argument get filled into. And (&) is just flipped ($).)

like image 15
shachaf Avatar answered Nov 10 '22 09:11

shachaf