Parsing Haskell中的干燥器通用类型

Parsing Haskell中的干燥器通用类型,parsing,haskell,types,dry,dto,Parsing,Haskell,Types,Dry,Dto,假设我有以下类型: newtype AddressID = {unAddressId :: UUID } deriving (Generic, Show, Eq) newtype PersonID = {unPersonId :: UUID } deriving (Generic, Show, Eq) data Address = { addressId :: AddressID} deriving (Generic, Show, Eq) data Person = { personId ::

假设我有以下类型:

newtype AddressID = {unAddressId :: UUID } deriving (Generic, Show, Eq)
newtype PersonID = {unPersonId :: UUID } deriving (Generic, Show, Eq)
data Address = { addressId :: AddressID} deriving (Generic, Show, Eq)
data Person  = { personId :: PersonID } deriving (Generic, Show, Eq)
data AddressDto = AddressDto { addressDtoId :: !UUID } deriving (Generic, Show, Eq)

data PersonDto = PersonDto { personDtoId :: !UUID } deriving (Generic, Show, Eq)

type AddressListDto = HashMap UUID AddressDto

type PersonListDto = HashMap UUID PersonDto

instance FromJSON PersonDto where
  parseJSON = genericParseJSON $ apiOptions "personDto"

instance ToJSON PersonDto where
  toJSON     = genericToJSON $ apiOptions "personDto"
  toEncoding = genericToEncoding $ apiOptions "personDto"

instance FromJSON AddressDto where
  parseJSON = genericParseJSON $ apiOptions "addressDto"

instance ToJSON AddressDto where
  toJSON     = genericToJSON $ apiOptions "addressDto"
  toEncoding = genericToEncoding $ apiOptions "addressDto"
具有以下实用程序功能:

fromAddress :: Address -> AddressDto
fromAddress Address{..} = AddressDto {addresDtoId = unAddressId addressId}

fromPerson :: Person -> PersonDto
fromAddress Person{..} = PersonDto {personDtoId = unPersonId personId}

appendPerson :: PersonListDto -> Person -> PersonListDto
appendPerson pld per = insert (personDtoId $ fromPerson per) (fromPolicy per) pld


fromPersonList :: [Person] -> PersonListDto
fromPersonList = foldl appendPerson empty


appendAddress :: AddressListDto -> Address -> AddressListDto
appendAddress ald addr = insert (addressDtoId $ fromAddress addr) (fromAddress addr) ald


fromAddressList :: [Address] -> AddressListDto
fromAddressList = foldl appendAddress empty
这段代码工作得很好,但是它非常重复,并且随着这些内部类型的数量而膨胀。这些函数是相同的,但可以跨不同的对象进行操作,助手函数的命名约定是区分不同实现的唯一方法


Haskell是如何跨这些类型创建更通用的可重用助手函数的?如何创建
fromtentity
fromto
appendentitytolist
fromtentitylist
函数?有没有一种方法可以在不重复自己的情况下对JSON实例进行编码(实际上完全相同)?类型类在这里合适吗?有没有关于如何使用它们的好材料

对于JSON实例,是的,您完全可以减少样板文件,但需要花费一些设置。最大的诀窍是相对较新的GHC功能,
deringvia
。这允许您解释如何根据另一种类型的实例为一种类型创建实例

{-# language DeriveGeneric, DerivingVia, StandaloneDeriving
 , TypeFamilies, UndecidableInstances, ScopedTypeVariables
 , PolyKinds, TypeApplications #-}

-- A newtype wrapper for the type we want an instance for
newtype Optionish a = Optionish a

-- A FromJSON instance for Optionish
instance
  ( Generic a
  , Rep a ~ M1 i c f
  , Datatype c
  , GFromJSON Zero f
  )
  => FromJSON (Optionish a) where
  parseJSON v = Optionish <$> genericParseJSON (apiOptions (lower name)) v
    where
      name = datatypeName (Proxy3 @c)

data Proxy3 d f a = Proxy3

lower :: String -> String
lower "" = ""
lower (x:xs) = toLower x : xs

您可以对
ToJSON

做同样的事情,您可以用泛型废弃大部分样板文件。你可以从这里开始:你考虑过把你的问题带到代码评审吗?@MichaelLitchard,我不认为这个问题最适合那个网站。可能是错误的,但“我不喜欢我解决问题的方法;我如何才能找到新的方法?”似乎更像是这里的一个问题。什么是
apipoptions
?还有一个软件包@李耀霞,你为什么不写一个答案,说明如何处理这个问题?我认为这一提及足以让感兴趣的读者了解。如果我只是把这个链接放在一个答案里,有人会告诉我,让它更具实质性。。。
deriving via Optionish PersonDto
  instance FromJSON PersonDto