如何用Haskell的Generics/Data.Data提取newtype信息生成文档表格
你之前无法获取内部类型,是因为constrType函数返回的是构造器所属的外层类型,你反复调用该函数只会循环获取newtype本身的元信息,没有深入到构造器的参数类型层面。
你的需求完全可以通过Data.Data实现,也可以用GHC.Generics实现,两种方案的实现代码如下:
方案1:使用Data.Data(更简洁)
只要为你的newtype派生Data和Typeable实例,即可通过gmapQ遍历构造器的参数,拿到内部包装的类型信息:
{-# LANGUAGE DeriveDataTypeable #-} {-# LANGUAGE ScopedTypeVariables #-} import Data.Data import Data.Int import Data.Word import Data.Proxy data MyRowRepresentation = MyRowRepresentation String -- newtype名称 String -- 包装的内部类型名称 deriving (Show) toDocRow :: forall a. Data a => Proxy a -> MyRowRepresentation toDocRow _ = MyRowRepresentation (tyconName $ dataTypeOf (undefined :: a)) -- 取外层newtype的名称 (show $ head $ gmapQ typeOf (undefined :: a)) -- 取构造器第一个参数的类型(newtype只有一个参数) -- 你的newtype定义,注意修正你原示例里构造器名和类型名不一致的笔误 newtype MyType0 = MyType0 Int16 deriving (Data, Typeable) newtype MyType1 = MyType1 Int8 deriving (Data, Typeable) newtype MyType2 = MyType2 Int32 deriving (Data, Typeable) newtype MyType3 = MyType3 Word8 deriving (Data, Typeable) main :: IO () main = do let table = [ toDocRow (Proxy :: Proxy MyType0), toDocRow (Proxy :: Proxy MyType1), toDocRow (Proxy :: Proxy MyType2), toDocRow (Proxy :: Proxy MyType3) ] print table -- 后续可以把table转为markdown表格写入文件
运行后输出的结构和你需要的完全一致:
[MyRowRepresentation "MyType0" "Int16",MyRowRepresentation "MyType1" "Int8",MyRowRepresentation "MyType2" "Int32",MyRowRepresentation "MyType3" "Word8"]
方案2:使用GHC.Generics
如果你偏好使用GHC.Generics而非Data.Data,可以参考以下实现:
{-# LANGUAGE DeriveGeneric #-} {-# LANGUAGE FlexibleInstances #-} {-# LANGUAGE ScopedTypeVariables #-} {-# LANGUAGE TypeApplications #-} {-# LANGUAGE UndecidableInstances #-} import Data.Proxy import GHC.Generics import Data.Int import Data.Word import Data.Typeable data MyRowRepresentation = MyRowRepresentation String -- newtype名称 String -- 包装的内部类型名称 deriving (Show) class GetDocRow a where toDocRow :: Proxy a -> MyRowRepresentation instance (Generic a, GetDocRowRep (Rep a)) => GetDocRow a where toDocRow _ = getDocRowRep (Proxy @(Rep a)) class GetDocRowRep f where getDocRowRep :: Proxy f -> MyRowRepresentation instance (Datatype d, GetDocRowCon f) => GetDocRowRep (D1 d f) where getDocRowRep _ = MyRowRepresentation (datatypeName @d undefined) (getDocRowCon (Proxy @f)) class GetDocRowCon f where getDocRowCon :: Proxy f -> String instance (Selector s, Typeable a) => GetDocRowCon (C1 c (S1 s (Rec0 a))) where getDocRowCon _ = show $ typeRep (Proxy @a) -- newtype派生Generic即可 newtype MyType0 = MyType0 Int16 deriving (Generic) newtype MyType1 = MyType1 Int8 deriving (Generic) newtype MyType2 = MyType2 Int32 deriving (Generic) newtype MyType3 = MyType3 Word8 deriving (Generic) main :: IO () main = do let table = [ toDocRow (Proxy :: Proxy MyType0), toDocRow (Proxy :: Proxy MyType1), toDocRow (Proxy :: Proxy MyType2), toDocRow (Proxy :: Proxy MyType3) ] print table
两种方案都可以满足你的需求,生成对应的结构后你可以自行拼接为markdown表格写入文件即可。
内容的提问来源于stack exchange,提问作者user2633351
相关产品推荐
相关产品推荐

