使用同一镜头与多个数据构造函数

3
假设我有一个这样的类型:
data Stock = Stock {
               _stockSymbol :: String,
               _stockFairValue :: Float,
               _stockBuyAt :: Float,
               _stockCurrentPrice :: Float
             } |
             Etf {
               _etfSymbol :: String,
               _etfFairValue :: Float,
               _etfBuyAt :: Float,
               _etfCurrentPrice :: Float
             } deriving (Eq)

股票ETF 都有相同的字段。现在我想访问其中一个的符号:

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

我可以使用类型类来完成这个任务,但我想知道是否有lens包可以自动为我构建此镜头?我已经查看了makeFields函数,但似乎只适用于拥有单独定义的构造函数的情况:

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

有没有办法在保持它们为同一类型的情况下完成此操作?
编辑:这个可行:
makeLensesFor [("_stockSymbol", "symbol"),
               ("_etfSymbol", "symbol"),
               ("_stockFairValue", "fairValue"),
               ("_etfFairValue", "fairValue"),
               ("_stockBuyAt", "buyAt"),
               ("_etfBuyAt", "buyAt"),
               ("_stockCurrentPrice", "currentPrice"),
               ("_etfCurrentPrice", "currentPrice")
               ] ''Stock

我不确定是否有内置的方法,可以避免手动书写字段。


3
你可以将数据类型定义为 Stock { isEtf :: Bool, _stockSymbol :: String, ... }。如果所有字段都相同,唯一的区别是构造函数,那么只需将构造函数作为字段即可。我猜测 lens 库不支持你尝试做的事情,因为多构造函数记录通常不被鼓励。 你可以尝试在两个构造函数中使用相同的字段名称,这可能会起作用。 - bheklilr
@bheklilr 我有一个可行的例子,请看我的编辑。我可以像你说的那样使用字段,但是使用数据构造函数似乎更好。为什么镜头库不鼓励多构造函数记录? - Vlad the Impala
1
Haskell 社区普遍不鼓励使用它们,因为它们可能会带来危险。例如,如果我调用 _etfBuyAt (Stock "" 0 0 0),那会怎样呢?引入部分函数会降低安全性,这可能导致应用程序崩溃。 - bheklilr
啊,明白了。谢谢!跟进问题:假设我将其分成两种数据类型,StockEtf,但由于镜头,我可以在两者上使用相同的函数。如何编写一个可以接受任一一个类型的函数呢?就像 getSymbol :: EitherStockOrEtf -> String - Vlad the Impala
@VladTheImpala 你为什么想要这样做呢?这会让你的生活变得更加困难。 - Benjamin Hodgson
我认为 lens 实际上可以减少多构造函数记录的问题,因为它确保仅在字段未始终定义时才派生 Traversal - Ørjan Johansen
1个回答

2

并不是要反驳bheklilr的评论,但你可以这样做:

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

哦,我不知道多个数据构造函数可以共享相同的字段名称。谢谢! - Vlad the Impala
1
这个是否使用了新的(自7.10版本起)OverloadedRecords扩展呢? - crockeea
2
@Eric:不是的,是Haskell 98。(“数据声明可以在多个构造函数中使用相同的字段标签,只要在类型同义词扩展后,在所有情况下该字段的类型都相同。”)你可能被OP写“数据类型”而实际上他们是指“数据构造函数”所误导。 - Reid Barton

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接