This is going to be a short post, maybe even a tweet size post. I’m going to highlight how to generate lenses for third party libraries because when I was searching for this information it wasn’t that easy to find.
My concern was less on the mechanics on how to generate them but more on best practices, I wasn’t sure if generating lenses for data types I don’t own is good practice. When I asked in fp-chat, I was told to “Go hog wild!” and didn’t get any opposing views. So that gave me the peace of mind to do this.
I’m going to use the purescript language library as an example since it’s the most recent library I had to work with.
I’m going to use makeLenses
from Control.Lens.TH
.
The default configuration of makeLenses
is it will only generate
lenses for record fields that are prefixed with an underscore.
data Person = Person
firstName :: Text -- does not generate lens for this field.
{ _lastName :: Text -- generates the lens for this field.
,deriving ( Eq, Show )
}
'Person makeLenses '
Before I can start generating lenses for the types I don’t own, I have to change
the makeLenses
configuration a little bit.
module Util where
import Language.Haskell.TH
import Control.Lens.Operators
import Control.Lens.TH
mkCustomLenses :: Name -> DecsQ
= makeLensesWith
mkCustomLenses $ lensRules
& lensField
.~ (\_ _ name -> [TopName ( mkName $ nameBase name ++ "L" )]) -- you can append whatever suits your code base here, I chose to append "L"
It has to be in a separate module from where you generate the lenses because the compiler will complain with this error
GHC stage restriction:
`mkCustomLenses' is used in a top-level splice, quasi-quote, or annotation,
and must be imported, not defined locally
After that setup you can start generating lenses!
module Optics where
-- util
import Util
-- purescript
import Language.PureScript.CST.Types
'Declaration
makePrisms ''Guarded
makePrisms ''Expr
makePrisms '
'ValueBindingFields
mkCustomLenses ''Name
mkCustomLenses ''Ident
mkCustomLenses ''Labeled
mkCustomLenses ''Where
mkCustomLenses ''Wrapped
mkCustomLenses ''Separated
mkCustomLenses ''Module mkCustomLenses '
Now, if I want to manipulate Separated
I can do something like
updateToEmptyList :: Separated a -> Separated a
= s & sepTailL .~ [] updateToEmptyList s
When it comes to sum types you can just use makePrisms
to generate prisms.