I am using lens with makeFields TH function. This allows me to do something like this:
{-# LANGUAGE MultiParamTypeClasses #-} {-# LANGUAGE FunctionalDependencies #-} {-# LANGUAGE TemplateHaskell #-} import Control.Lens data Person = Person { _personFirstName :: String , _personLastName :: String , _personEmail :: String } data Corp = Corp { _corpName :: String , _corpEmail :: String } makeFields ''Person makeFields ''Corp main = let myGuy = Person "Test" "Guy" "email@account.com" myCorp = Corp "ABC" "sales@abc.com" in putStrLn $ "personal email: " ++ (myGuy^.email) ++ "corp email: " ++ (myCorp^.email)
What happens here is that the makeFields function creates HasName and HasEmail class types that have one member (name or email address). He also creates instances for them for these types. MultiParamTypeClasses and functional dependencies allow you to "overload" these elements for different data types.
makeFields, by convention, is going to do this for each record constructor that starts with an underscore, and the name it chooses for the field is the balance of the name of the constructor starting with the first capital letter. Thus, _corpEmail creates the email field.
Now these โfieldsโ are lenses, and there are many functions that you can apply with them. To simply get the value from a record, you can use the "view" function, for example:
view email myGuy
There is an inverse function called (^.), Which takes the record as the first parameter, and the lens as the second. I personally prefer this style in many cases, but I still often use representation, especially in the dotless style.
The LOT lens is more than just this feature - I personally find this feature irresistible, but in fact the most important thing that it does for you is letting you compose these lenses to make updates in the depths of the data structures. Composition is simple with an operator (.), Like any other function; so if we assume that my example had the _corpPresident :: Person field, I could do this:
putStrLn $ myCorp^.president.email
Or - change this nested value:
let newCorp = set president.email "changedemail@address.com" myCorp
Or the same thing (with great operator frenzy):
let newCorp = myCorp & president.email .~ "changedemail@address.com"
There is much more, and I barely scratched the surface. The hackage docs are good, but I think the best way to get started is with a field guide that is in Github repository read mode .