如前所述,这不是直接可能的,但我想就提议的解决方案说几件事:
如果这两个字段明显不同,那么您总是想知道自己在使用哪个字段。这里的“明显不同”是指永远不会有对任何一个领域做同样的事情是有意义的情况。鉴于此,过度消歧并不是不受欢迎的,因此您希望将 qualified imports 作为标准方法,或者如果这更符合您的口味,则希望使用 field disambiguation 扩展。或者,作为一个非常简单(而且有点难看)的选项,只需手动为字段添加前缀,例如deviceArrayName 而不仅仅是 name。
如果这两个字段在某种意义上相同,那么能够以同质的方式对待它们是有意义的;理想情况下,您可以编写一个选择name 字段的多态函数。在这种情况下,一种选择是使用 type class 来表示“命名的事物”,其功能可以让您访问任何适当类型的 name 字段。这里的一个主要缺点是,除了琐碎的类型约束的扩散和可怕的单态限制可能令人头疼的问题之外,您还失去了使用记录语法的能力,这开始破坏整个观点。
我还没有看到建议的类似字段的另一个主要选项是将name 字段提取到单个参数化类型中,例如data Named a = Named { name :: String, item :: a }。 GHC itself uses this approach for source locations in syntax trees,虽然它不使用记录语法,但想法是一样的。这里的缺点是,如果您有Named DeviceArray,现在访问bytes 字段需要经过两层记录。如果你想用函数更新bytes 字段,你会遇到这样的事情:
addBytes b na = na { item = (item na) { bytes = b + bytes (item na) } }
呃。有一些方法可以稍微缓解这个问题,但在我看来,它们仍然不知道。像这样的情况是我一般不喜欢记录语法的原因。所以,作为最后的选择,一些 Template Haskell 魔法和the fclabels package:
{-# 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。