Vlad the Impala
Vlad the Impala

Reputation: 15882

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.

Upvotes: 3

Views: 462

Answers (1)

Reid Barton
Reid Barton

Reputation: 15029

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)

Upvotes: 2

Related Questions