我正在尝试使用GHC.Generics
编写一个泛型函数,该函数将返回值中使用的所有数据类型名称。
这是我到目前为止所做的:
{-# 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)
缺少的唯一部分是M1 i c a
实例定义。
如何调用datatypeName
来获取该类型的名称?
我正在跟随Stephan Diehl的“我希望我知道什么......”关于泛型的博文[1]
答案 0 :(得分:3)
首先提示,如果您还不知道这一点:您可以在GHCi中按Rep
查看类型:kind!
- s。例如:
> :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])))
关于实际问题,对于当前作业datatypeName
不适用,我们可以使用typeOf
中的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)
示例:
> data Foo = Foo Int Bool () deriving (Generic)
> names $ Foo 0 True ()
[Int,Bool,()]
请注意,此实现不是递归的,它只是查看最顶层构造函数的字段。
> names [0, 1]
[Integer, [Integer]]
递归版本将涉及更多的机制,因为某些类型的表示很容易导致天真实现中的无限循环,因此我们必须跟踪访问的字段。