如何使用GHC.Generics的selName函数?

leo*_*leo 4 generics haskell

我正在寻找一个关于使用Haskell GHC.Generics包的selName函数的简单示例.

考虑以下记录类型:

{-# language DeriveGeneric #-}

data Person = Person {
    firstName :: String
  , lastName  :: String
  , age       :: Integer
  } deriving(Generic
Run Code Online (Sandbox Code Playgroud)

如何使用selName函数获取firstName选择器的名称?

ben*_*ofs 12

以下代码需要以下扩展名:

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}
Run Code Online (Sandbox Code Playgroud)

首先,您可以使用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))))
    ()
Run Code Online (Sandbox Code Playgroud)

您正在寻找的选择器类型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.
Run Code Online (Sandbox Code Playgroud)

我们需要一种方法将Person类型传递给我们的函数,该函数获取第一个选择器的名称.我们可以使用一个Proxy类型来实现它,它只有一个构造函数,但是用一个类型"标记":(你也可以使用一个参数undefined :: Person并忽略它,但这样保证你只能忽略它).

data Proxy a = Proxy -- also provided by the `tagged` hackage package
Run Code Online (Sandbox Code Playgroud)

现在,类型selNameselName :: t s (f :: * -> *) a -> [Char],所以你需要一个匹配模式的类型t s (f :: * -> *) a来使用这个函数.我们使用相同的技巧来创建一个SelectorProxy只有一个构造函数但具有所需形式的技巧:

data SelectorProxy s (f :: * -> *) a = SelectorProxy
type SelectorProxy' s = SelectorProxy s Proxy ()
Run Code Online (Sandbox Code Playgroud)

最后,我们准备编写获取选择器名称的函数:

firstSelectorName :: forall a. (Generic a, Selector (FirstSelector (Rep a))) => Proxy a -> String
firstSelectorName Proxy = selName (SelectorProxy :: SelectorProxy' (FirstSelector (Rep a)))
Run Code Online (Sandbox Code Playgroud)

如果你在GHCi中加载它,你可以看到它的工作原理:

?> firstSelectorName (Proxy :: Proxy Person)
"firstName"
Run Code Online (Sandbox Code Playgroud)

  • 哇!不像我想的那么容易.非常感谢你这个详细的例子,它将帮助我(希望其他许多人!)掌握GHC.Generics! (2认同)