我正在寻找一个关于使用Haskell GHC.Generics包的selName函数的简单示例。
考虑以下记录类型:
{-# language DeriveGeneric #-}
data Person = Person {
firstName :: String
, lastName :: String
, age :: Integer
} deriving(Generic
如何使用selName函数获取firstName选择器的名称?
答案 0 :(得分:12)
以下代码需要以下扩展名:
{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
首先,您可以使用GHCi找出您的人员类型的通用表示形式:
λ> :kind! Rep Person ()
Rep Person () :: *
= M1
D
Main.D1Person
(M1
C
Main.C1_0Person
(M1 S Main.S1_0_0Person (K1 R [Char])
:*: (M1 S Main.S1_0_1Person (K1 R [Char])
:*: M1 S Main.S1_0_2Person (K1 R Integer))))
()
您要查找的选择器类型为Main.S1_0_1Person
。要提取它,您可以使用类型族:
type family FirstSelector (f :: * -> *) :: *
type instance FirstSelector (M1 D x f) = FirstSelector f
type instance FirstSelector (M1 C x f) = FirstSelector f
type instance FirstSelector (a :*: b) = FirstSelector a -- Choose first selector
type instance FirstSelector (M1 S s f) = s
-- Note: this doesn't support types with multiple constructors.
-- You'll get a type error in that case.
我们需要一种方法将Person
类型传递给我们的函数,该函数获取第一个选择器的名称。我们可以使用Proxy
类型来实现它,它只有一个构造函数,但是#34;标记了#34;使用类型:(您也可以使用参数undefined :: Person
并忽略它,但这样保证您只能忽略它。)
data Proxy a = Proxy -- also provided by the `tagged` hackage package
现在,selName
的类型为selName :: t s (f :: * -> *) a -> [Char]
,因此您需要一个与模式t s (f :: * -> *) a
匹配的类型才能使用该函数。我们使用相同的技巧来创建一个只有一个构造函数的SelectorProxy
,但它是必需的形式:
data SelectorProxy s (f :: * -> *) a = SelectorProxy
type SelectorProxy' s = SelectorProxy s Proxy ()
最后,我们准备编写获取选择器名称的函数:
firstSelectorName :: forall a. (Generic a, Selector (FirstSelector (Rep a))) => Proxy a -> String
firstSelectorName Proxy = selName (SelectorProxy :: SelectorProxy' (FirstSelector (Rep a)))
如果你在GHCi中加载它,你可以看到它有效:
λ> firstSelectorName (Proxy :: Proxy Person)
"firstName"