【问题标题】:Use the same lens with multiple data constructors使用具有多个数据构造函数的同一个镜头
【发布时间】:2015-03-27 20:39:27
【问题描述】:

假设我有这样的类型:

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

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

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

我可以使用 typeclass 来做到这一点,但我想知道 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

不确定是否有一种内置方式我不必写出这些字段。

【问题讨论】:

  • 您能否将数据类型定义为Stock { isEtf :: Bool, _stockSymbol :: String, ... }?如果所有字段都相同并且不同之处在于构造函数,那么只需将构造函数设为字段即可。我猜测镜头库不支持您尝试执行的操作,因为通常不鼓励使用多构造函数记录。不过,您可以尝试为两个构造函数使用相同的字段名称,这可能会奏效。
  • @bheklilr 我有一个工作示例,请参阅我的编辑。我可以像你说的那样使用一个字段,但是拥有数据构造函数似乎更好。为什么镜头库不鼓励多构造函数记录?
  • haskell 社区通常不鼓励他们,他们可能很危险。例如,如果我打电话给_etfBuyAt (Stock "" 0 0 0) 怎么办?引入部分函数会失去安全性,这可能会导致应用程序崩溃。
  • 啊,有道理。谢谢!后续问题:假设我将其分为两种数据类型,StockEtf,但由于有了镜头,我可以在两者上使用相同的功能。我如何编写一个可以接受任何一个的函数?喜欢getSymbol :: EitherStockOrEtf -> String
  • @VladTheImpala 你为什么要这样做?你让自己的生活变得艰难

标签: haskell haskell-lens


【解决方案1】:

不同意 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)

【讨论】:

  • 哦,我不知道多个数据构造函数可以共享相同的字段名称。谢谢!
  • 这确实使用了新的(从 7.10 开始)OverloadedRecords 扩展,不是吗?
  • @Eric:不,Haskell 98。(“只要字段的类型在类型同义词扩展后的所有情况下都相同,数据声明就可以在多个构造函数中使用相同的字段标签。” ) 当 OP 表示“数据构造函数”时,您可能会被 OP 写成“数据类型”误导。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2014-03-12
  • 2015-06-25
  • 2012-06-22
相关资源
最近更新 更多