编写用于 GHC.Generics 的类型级函数
writing a type-level function for use with GHC.Generics
我正在尝试使用 GHC.Generics
编写一个通用函数,它将 return 一个值中使用的所有数据类型名称。
这是我目前的情况:
{-# 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 关于泛型的 "What I Wish I Knew..." 博客 post [1]
第一个提示,如果您还不知道:您可以在 GHCi 中通过 :kind!
查看类型 Rep
-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]]
递归版本将涉及更多的机制,因为某些类型的表示很容易导致天真的实现中的无限循环,因此我们必须跟踪访问过的字段。
我正在尝试使用 GHC.Generics
编写一个通用函数,它将 return 一个值中使用的所有数据类型名称。
这是我目前的情况:
{-# 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 关于泛型的 "What I Wish I Knew..." 博客 post [1]
第一个提示,如果您还不知道:您可以在 GHCi 中通过 :kind!
查看类型 Rep
-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]]
递归版本将涉及更多的机制,因为某些类型的表示很容易导致天真的实现中的无限循环,因此我们必须跟踪访问过的字段。