寻找一种更好的方法来编写将类型构造函数作为参数的函数

Looking for a better way to write a function that takes a type constructor as argument

我有一个 Haskell 仆人应用程序。我想从文件中读取并用文件的内容填充数据库。我有的是这个

userList :: IO [User]
productList :: IO [Product]

data User = User { age :: Int, fname :: String, lname :: String }
data Product = Product { title :: String, description :: String }
data Item = UserI User | ProductI Product

listUsers :: Handler [Entity User]
listProducts :: Handler [Entity Product]

hydrateUserDB :: Handler [Entity User]
hydrateUserDB = do
    items <- liftIO userList
    let list = fmap User items
    traverse_ createUser list
    listUsers

hydrateProductDB :: Handler [Entity Product]
hydrateProductDB = do
    items <- liftIO productList
    let list = fmap Product items
    traverse_ createProduct list
    listProducts

现在我想要一个可以接受 User 或 Product 并产生与上述类似结果的函数。 类似于:

hydrateDB :: Handler [Entity a]
hydrateDB =
    \alist con createItem listItems -> do
    items <- liftIO alist
    let list = fmap con items
    traverse_ createItem list
    listItems

这也许是 typeclasses 的一个很好的用途。将一个版本与另一个版本不同的东西放在 class 中。设计或许可以改进,但这是第一步:

class DBItem a where
    itemList :: IO [a]
    createItem :: a -> Handler ()
    listItems :: Handler [Entity a]

instance DBItems User where
    itemList = userList
    createItem = ...
    listItems = listUsers

instance DBItems Product where
    itemList = productList
    ...

hydrateDB :: (DBItem a) => Handler [Entity a]
hydrateDB = do
    items <- liftIO itemList
    traverse_ createItem items
    listItems

(我做了一些更改以使类型有意义,但你明白了)