Writing a type level function for use with GHC.Generics

I am trying to use GHC.Generics

to write a generic function that will return all data type names used in a value.

Here's what I have so far:

{-# LANGUAGE DefaultSignatures, DeriveGeneric, TypeOperators, FlexibleContexts #-}
{-# LANGUAGE MonomorphismRestriction #-}

module Lib4 where

import GHC.Generics

class Names f where
  names' :: f a -> [String]

instance Names U1 where
  names' _ = []

instance (Names a, Names b) => Names (a :+: b) where
  names' (L1 x) = names' x
  names' (R1 x) = names' x

instance (Names a, Names b) => Names (a :*: b) where
  names' (a :*: b) = names' a ++ names' b

instance Names (M1 i c a) where
  names' (M1 x) = ...  -- use datatypeName here?

names x = names' (from x)

      

The only part that is missing is in the instance definition M1 i c a

.

How do I call datatypeName

to get the type name?

I follow Stefan Diehl "What I want to know ..." on the blog at Generics [1]

[1] http://dev.stephendiehl.com/hask/#generic

+3


source to share


1 answer


First tip if you don't already know it: you can look at the Rep

-s type :kind!

in GHCi. For example:

> :kind! Rep [Int]
Rep [Int] :: * -> *
= D1
    GHC.Generics.D1[]
    (C1 GHC.Generics.C1_0[] U1
     :+: C1
           GHC.Generics.C1_1[]
           (S1 NoSelector (Rec0 Int) :*: S1 NoSelector (Rec0 [Int])))

      

As for the actual question, it is datatypeName

not applicable for the current task , instead we can restore the field types using typeOf

from Data.Typeable

.

{-# LANGUAGE MonomorphismRestriction #-}

import GHC.Generics
import Data.Typeable

class Names f where
  names' :: f a -> [TypeRep]

instance Names U1 where
  names' _ = []

instance (Names a, Names b) => Names (a :+: b) where
  names' (L1 x) = names' x
  names' (R1 x) = names' x

instance (Names a, Names b) => Names (a :*: b) where
  names' (a :*: b) = names' a ++ names' b

instance Names f => Names (M1 i c f) where
  names' (M1 fa) = names' fa

instance (Typeable t) => Names (Rec0 t) where
  names' (K1 x) = [typeOf x]

names x = names' (from x)  

      

Example:



> data Foo = Foo Int Bool () deriving (Generic)
> names $ Foo 0 True ()
[Int,Bool,()]

      

Note that this implementation is not recursive, it just looks at the fields of the topmost constructor.

> names [0, 1]
[Integer, [Integer]]

      

The recursive version will include more machines, since some types have views that easily lead to infinite loops in naive implementations, and therefore we will need to keep track of the fields visited.

+3


source







All Articles