简体   繁体   中英

How to use the selName function of GHC.Generics?

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 :: ts (f :: * -> *) a -> [Char] , so you need a type that matches the pattern ts (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"

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM