推断两个记录中的公共字段的类型

nor*_*tpy 3 haskell purescript recordtype row-polymorphism

如果这是一个愚蠢的问题,请耐心等待.如何键入一个带有两个记录并返回其公共字段数组的泛型函数?

比方说我有:

type A = { name :: String, color :: String }
type B = { name :: String, address :: Address, color :: String }

myImaginaryFunction :: ???
-- should return ["name", "color"] :: Array of [name color]
Run Code Online (Sandbox Code Playgroud)

我想编写一个函数,它接受任何两种类型的记录并返回一个公共字段数组.一个haskell解决方案也可以工作.

K. *_*uhr 5

要在Haskell中表达具有公共字段的两种记录类型,您需要GHC扩展:

{-# LANGUAGE DuplicateRecordFields #-}
Run Code Online (Sandbox Code Playgroud)

并且要反省字段的名称,您需要基于Data类的泛型:

{-# LANGUAGE DeriveDataTypeable #-}
import Data.Data ( Data, Typeable, DataRep(AlgRep), dataTypeRep
                 , dataTypeOf, constrFields)
import Data.List (intersect)
import Data.Proxy (Proxy(..), asProxyTypeOf)
Run Code Online (Sandbox Code Playgroud)

这将允许您使用相同的字段名称定义两种数据类型:

data Address = Address String deriving (Typeable, Data)
data A = A { name :: String, color :: String }
    deriving (Typeable, Data)
data B = B { name :: String, address :: Address, color :: String}
    deriving (Typeable, Data)
Run Code Online (Sandbox Code Playgroud)

然后你可以使用以下方法检索字段名称:

fieldNames :: (Data t) => Proxy t -> [String]
fieldNames t = case dataTypeRep $ dataTypeOf $ asProxyTypeOf undefined t of
  AlgRep [con] -> constrFields con
Run Code Online (Sandbox Code Playgroud)

并获得以下公共字段:

commonFields :: (Data t1, Data t2) => Proxy t1 -> Proxy t2 -> [String]
commonFields t1 t2 = intersect (fieldNames t1) (fieldNames t2)
Run Code Online (Sandbox Code Playgroud)

之后,以下内容将起作用:

ghci> commonFields (Proxy :: Proxy A) (Proxy :: Proxy B)
["name", "color"]
ghci>
Run Code Online (Sandbox Code Playgroud)

请注意,fieldNames上面的实现假定只会考虑具有单个构造函数的记录类型.Data.Data如果要概括它,请参阅文档.

现在,因为你是一个帮助吸血鬼,我知道你会要求一个类型级别的功能,即使你在问题中没有说什么需要一个类型级别的功能!事实上,我可以看到你已经添加了一个关于你如何感兴趣以某种方式返回一个数组的评论,name | color虽然在Haskell中没有这样的东西存在,即使你在你的问题中明确地说你期望得到了术语级答案["name", "color"].

尽管如此,可能还有非吸血鬼有类似的问题,也许这个答案会帮助他们.