正如已经指出的,这不可能直接实现,但我想就建议的解决方案说几句话:
如果这两个字段是明显不同,无论如何你都会想知道你正在使用哪个。这里所说的“明显不同”是指在任何情况下都不可能对任何一个领域做同样的事情。鉴于此,过度的歧义并不是真正不受欢迎,所以你想要合格进口作为标准方法,或字段消歧扩展如果这更符合您的口味。或者,作为一个非常简单(并且有点难看)的选项,只需手动为字段添加前缀,例如deviceArrayName
而不是仅仅name
.
如果这两个字段在某种意义上是一样的东西,能够以同质的方式对待它们是有意义的;理想情况下,您可以编写一个多态函数来选择name
场地。在这种情况下,一种选择是使用类型类别对于“命名事物”,具有可让您访问name
任何适当类型的字段。这里的一个主要缺点是,除了琐碎类型约束的激增和可怕的单态限制可能带来的麻烦之外,您还失去了使用记录语法的能力,这开始破坏整个要点。
类似字段的另一个主要选项(我还没有看到建议)是提取name
字段输出为单个参数化类型,例如data Named a = Named { name :: String, item :: a }
. GHC 本身使用这种方法来定位语法树中的源位置 http://www.haskell.org/ghc/docs/latest/html/libraries/ghc/SrcLoc.html#t:Located,虽然它不使用记录语法,但想法是相同的。这里的缺点是如果你有一个Named DeviceArray
,访问bytes
字段现在需要经过两层记录。如果您想更新bytes
带有函数的字段,你会遇到这样的问题:
addBytes b na = na { item = (item na) { bytes = b + bytes (item na) } }
啊。有一些方法可以稍微缓解这个问题,但在我看来,它们仍然不是主意。像这样的情况就是我一般不喜欢记录语法的原因。因此,作为最后的选择,一些哈斯克尔模板魔法和the fclabels package http://hackage.haskell.org/package/fclabels:
{-# LANGUAGE TemplateHaskell #-}
import Control.Category
import Data.Record.Label
data Named a = Named
{ _name :: String,
_namedItem :: a }
deriving (Eq, Show, Data, Typeable)
data DeviceArray = DeviceArray { _bytes :: Int }
deriving (Eq, Show, Data, Typeable)
data MakefileParams = MakefileParams { _makefileParams :: [MakeParam] }
deriving (Eq, Show, Data, Typeable)
data MakeParam = MakeParam { paramText :: String }
deriving (Eq, Show, Data, Typeable)
$(mkLabels [''Named, ''DeviceArray, ''MakefileParams, ''MakeParam])
不介意MakeParam
生意,我只是需要一个可以做点什么的地方。无论如何,现在您可以像这样修改字段:
addBytes b = modL (namedItem >>> bytes) (b +)
nubParams = modL (namedItem >>> makefileParams) nub
你还可以命名bytes
就像是bytesInternal
然后导出一个访问器bytes = namedItem >>> bytesInternal
如果你喜欢。