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

2

8

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.

Arber answered 16/6, 2013 at 10:49 Comment(0)
L
15

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 ($).)

Litalitany answered 16/6, 2013 at 11:10 Comment(1)
note that this is now broken with lens-4.4 which has modified the template-haskell related code.Anglesey
A
19

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)

Anglesey answered 25/10, 2014 at 14:5 Comment(0)
L
15

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 ($).)

Litalitany answered 16/6, 2013 at 11:10 Comment(1)
note that this is now broken with lens-4.4 which has modified the template-haskell related code.Anglesey

© 2022 - 2024 — McMap. All rights reserved.