数据类型的自定义通用表示形式 [英] Custom generic representation of the data type

查看:56
本文介绍了数据类型的自定义通用表示形式的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我需要通过给定的记录类型来呈现html表单.因此,我创建了类型类

I need to generically render an html form by the given record type. For this reason, I've created type class

class EntityRep a where
  toRep :: Proxy a -> [FieldRep]
  default toRep :: (Generic a, GEntityRep (Rep a)) => Proxy a -> [FieldRep]
  toRep _ = gtoRep (Proxy :: Proxy (Rep a))

其中

data FieldRep = FieldRep
  { fieldName :: String
  , fieldRequired :: Bool
  } deriving (Show)
class GEntityRep f where
  gtoRep :: Proxy f -> [FieldRep]

以及选择器表示形式的实例:

And the instance for the selector representation:

instance (Selector a, Required a) => GEntityRep (M1 S s (K1 R a)) where
  gtoRep _ = [FieldRep { fieldName = selName (undefined :: M1 S s (K1 R a) ())
                       , fieldRequired = isRequired (Proxy @a) }]

所以我要实现该功能

isRequired :: (Required a) => Proxy a -> Bool
--returns True, only if the type is (Maybe a) for all a

我的尝试是使用 Data.Data :

constrs :: forall a. (Data a) => Proxy a -> [Constr]
constrs _ = let dt = dataTypeOf (undefined :: a)
  in if isAlgType dt then dataTypeConstrs dt
     else []
isRequired :: forall a. (Data a) => Proxy a -> Bool
isRequired proxy =
     toConstr (Nothing :: Maybe ()) `elem` (constrs proxy)
  || toConstr (Just () :: Maybe ()) `elem` (constrs proxy)

但这不起作用,因为不同类型的构造可能相等.

But this does not work, because constrs for different types may be equal.

最后,进行以下记录

data PK a = PK a | Unset deriving (Data, Typeable, Show)
data ProductCategory = Clothes | Food deriving (Data, Typeable, Show)
data Product = Product { productName :: String
                       , productCategory :: ProductCategory
                       , productPrice :: Maybe Int } deriving (Generic, Show)
instance EntityRep Product

以下表达式

>>> toRep (Proxy @Product)

应该返回

[FieldRep{fieldName="productName"
         ,fieldRequired=True}
,FieldRep{fieldName="productCategory"
         ,fieldRequired=True}
,FieldRep{fieldName="productPrice"
         ,fieldRequired=False}]

我可以创建类型类

class Required a where
  isRequired :: Proxy a -> Bool

然后将其实现为各种类型,但是太麻烦了.并且所有实例都相同,除了也许是.

then implement it for the various of types, but it's so bothersome. And all instances will be identical, except Maybe a.

我们可以进行默认实现

instance Required a where
  isRequired _ = True

然后将其与也许是实例重叠:

instance {-# OVERLAPPING #-} Required (Maybe a) where
  isRequired _ = False

推荐答案

最终以 查看全文

登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆