Search code examples
haskellhaskell-lens

Use the same lens with multiple data constructors


Suppose I have a type like this:

data Stock = Stock {
               _stockSymbol :: String,
               _stockFairValue :: Float,
               _stockBuyAt :: Float,
               _stockCurrentPrice :: Float
             } |
             Etf {
               _etfSymbol :: String,
               _etfFairValue :: Float,
               _etfBuyAt :: Float,
               _etfCurrentPrice :: Float
             } deriving (Eq)

Stock and Etf both have the same fields. Now I want to access the symbol for one of them:

item ^. symbol -- don't care if stock or etf

I can do this with a typeclass, but I'm wondering if the lens package can build this lens for my automatically? I have looked at the makeFields function, but it seems that works if I have the constructors defined separately:

data Stock = Stock { ... }
data Etf   = Etf { ... }

Is there any way to do this while keeping them under the same type?

Edit: This works:

makeLensesFor [("_stockSymbol", "symbol"),
               ("_etfSymbol", "symbol"),
               ("_stockFairValue", "fairValue"),
               ("_etfFairValue", "fairValue"),
               ("_stockBuyAt", "buyAt"),
               ("_etfBuyAt", "buyAt"),
               ("_stockCurrentPrice", "currentPrice"),
               ("_etfCurrentPrice", "currentPrice")
               ] ''Stock

Not sure if there's a built-in way where I don't have to write the fields out.


Solution

  • Not to disagree with bheklilr's comment, but you could just do this:

    data Stock =
             Stock {
               _symbol :: String,
               _fairValue :: Float,
               _buyAt :: Float,
               _currentPrice :: Float
             } |
             Etf {
               _symbol :: String,
               _fairValue :: Float,
               _buyAt :: Float,
               _currentPrice :: Float
             } deriving (Eq)
    $(makeLenses ''Stock)