我有User
类型,表示保存在数据库中的用户。但是,在显示用户时,我只想返回这些字段的子集,因此我在没有hash
的情况下创建了不同的类型。创建用户时,系统会提供password
而不是hash
,因此我为此设置了另一种类型。
这显然是最糟糕的,因为我的类型之间存在大量重复。有没有更好的方法来创建几个共享某些字段的相关类型,但添加一些字段并删除其他字段?
{-# LANGUAGE DeriveGeneric #}
data User = User {
id :: String,
email :: String,
hash :: String,
institutionId :: String
} deriving (Show, Generic)
data UserPrintable = UserPrintable {
email :: String,
id :: String,
institutionId :: String
} deriving (Generic)
data UserCreatable = UserCreatable {
email :: String,
hash :: String,
institutionId :: String
} deriving (Generic)
data UserFromRequest = UserFromRequest {
email :: String,
institutionId :: String,
password :: String
} deriving (Generic)
-- UGHHHHHHHHHHH
答案 0 :(得分:2)
在这种情况下,我认为您可以用函数替换各种User
类型。因此,不是UserFromRequest
,而是:
userFromRequest :: Email -> InstitutionId -> String -> User
请注意,您还可以为Email
和InstitutionId
制作单独的类型,这可以帮助您避免一堆恼人的错误。这与将带有标记字段的记录作为参数的目的相同,同时还增加了一些额外的静态安全性。您可以将它们实现为newtypes:
newtype Email = Email String deriving (Show, Eq)
同样,我们可以将UserPrintable
替换为showUser
。
UserCreatable
可能有点尴尬,具体取决于您需要如何使用它。如果您只使用它作为参数并创建数据库行,那么您可以以相同的方式将其重构为函数。但是如果你真的需要这类型的东西,这不是一个好的解决方案。
在第二种情况下,你有几个不错的选择。一种方法是让id
成为Maybe
并每次检查一次。更好的方法是创建一个通用类型WithId a
,它只会向任何内容添加id
字段:
data WithId a = { id :: DatabaseId, content :: a }
然后使用否 User
的{{1}}类型,并使您的数据库函数与id
一起使用。