如何使用GHC.Generics的selName函数? [英] How to use the selName function of GHC.Generics?

查看:124
本文介绍了如何使用GHC.Generics的selName函数?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述



考虑以下记录类型:



  { - #language DeriveGeneric# - } 

data Person = Person {
firstName :: String
,lastName :: String
,age :: Integer
}派生(通用

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

解决方案

下面的代码需要以下扩展:

  { - #LANGUAGE FlexibleContexts# - } 
{ - #LANGUAGE DeriveGeneric# - }
{ - #LANGUAGE Typefamilies# - }
{ - #LANGUAGE TypeOperators# - }
{ - #$} >

首先,您可以使用GHCi来查找您的人员类型的通用表示:

 λ>:kin d!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 :: *  - > *):: * 
类型实例FirstSelector(M1:xF)= FirstSelector f
类型实例FirstSelector(M1 C xf)= FirstSelector f
类型实例FirstSelector(a:*:b)= FirstSelector a - 选择第一个选择器
类型实例FirstSelector(M1 S sf)= s
- 注意:这不支持具有多个构造函数的类型。
- 在这种情况下,您会收到类型错误。

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

  data Proxy a =代理 - 也由`tagged` hackage包提供

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

  data SelectorProxy s(f :: *  - > *)a = SelectorProxy 
SelectorProxy's = SelectorProxy s Proxy()



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

  firstSelectorName :: forall a。 (通用a,选择器(FirstSelector(Rep a)))=>代理a  - > String 
firstSelectorName Proxy = selName(SelectorProxy :: SelectorProxy'(FirstSelector(Rep a)))



<

 λ>>如果您在GHCi中加载它,您可以看到它的工作原理: firstSelectorName(Proxy :: Proxy Person)
firstName


I am looking for a simple example concerning the use of the selName function of the Haskell GHC.Generics package.

Considering the following record type:

{-# language DeriveGeneric #-}

data Person = Person {
    firstName :: String
  , lastName  :: String
  , age       :: Integer
  } deriving(Generic

How would be used the selName function to get the name of the firstName selector?

解决方案

The code below requires the following extensions:

{-# LANGUAGE FlexibleContexts #-}
{-# LANGUAGE DeriveGeneric #-}
{-# LANGUAGE ScopedTypeVariables #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE TypeFamilies #-}

First, you can use GHCi to find out the generic representation of your person type:

λ> :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))))
    ()

The selector type your looking for is Main.S1_0_1Person. To extract that, you can use a type family:

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.

We need a way to pass the Person type to our function that gets the name of the first selector. We can use a Proxy type to achieve that, which only has one constructor, but is "tagged" with a type: (you could also use an argument undefined :: Person and ignore it, but this way it's guarranted that you can only ignore it).

data Proxy a = Proxy -- also provided by the `tagged` hackage package

Now, the type of selName is selName :: t s (f :: * -> *) a -> [Char], so you need a type that matches the pattern t s (f :: * -> *) a to use the function. We use the same trick to create a SelectorProxy which has only one constructor, but is of the required form:

data SelectorProxy s (f :: * -> *) a = SelectorProxy
type SelectorProxy' s = SelectorProxy s Proxy ()

Finally, we're ready to write the function that gets the selector name:

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

And if you load that in GHCi, you can see that it works:

λ> firstSelectorName (Proxy :: Proxy Person)
"firstName"

这篇关于如何使用GHC.Generics的selName函数?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆