Say I have the following types:
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"
with the following utility functions:
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
This code works just fine, however it is very repeated, and balloons with the number of these internal types. The functions are identical, but operate across different objects, with a naming convention on helper functions being the only thing separating the different implementations.
What is the Haskell approach to creating more generically reusable helper functions across these types? How do I go about creating a fromEntity, fromDto, appendEntityToDtoList and fromEntityList functions? Is there a way I can encode (the virtually identical) To/FromJSON instances without repeating myself? Are Typeclasses appropriate here? Is there some good material on how to use them for this purpose?