如何在haskell中创建几个相关的数据类型?

如何在haskell中创建几个相关的数据类型?,haskell,types,Haskell,Types,我有一个User类型,它表示保存在数据库中的用户。但是,在显示用户时,我只想返回这些字段的一个子集,因此我创建了一个不同的类型,没有散列。在创建用户时,将提供一个密码,而不是散列,因此我为此创建了另一个类型 这显然是最糟糕的,因为我的类型之间有大量重复。有没有更好的方法来创建几个相关类型,这些类型共享一些字段,但添加一些字段并删除其他字段 {-# LANGUAGE DeriveGeneric #} data User = User { id :: String, email :: St

我有一个
User
类型,它表示保存在数据库中的用户。但是,在显示用户时,我只想返回这些字段的一个子集,因此我创建了一个不同的类型,没有
散列。在创建用户时,将提供一个
密码
,而不是
散列
,因此我为此创建了另一个类型

这显然是最糟糕的,因为我的类型之间有大量重复。有没有更好的方法来创建几个相关类型,这些类型共享一些字段,但添加一些字段并删除其他字段

{-# 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

在这种情况下,我认为可以用函数替换各种
用户
类型。因此,不要使用
UserFromRequest
,而是:

userFromRequest :: Email -> InstitutionId -> String -> User
请注意,您还可以为
电子邮件
机构ID
创建单独的类型,这将帮助您避免一系列恼人的错误。这与将带有标签字段的记录作为参数的目的相同,同时还增加了一点额外的静态安全性。您可以将这些实现为新类型:

newtype Email = Email String deriving (Show, Eq)
类似地,我们可以将
UserPrintable
替换为
showUser

usercreateable
可能有点麻烦,但这取决于您需要如何使用它。如果您所做的只是将其作为参数并创建一个数据库行,那么您可以用同样的方法将其重构为函数。但是如果你真的需要一堆东西的类型,这不是一个好的解决方案

在第二种情况下,您有几个不错的选择。一种方法是只需将
id
a
Maybe
,并每次检查它。更好的方法是创建一个id为A
的泛型类型
,它只向任何内容添加一个
id
字段:

data WithId a = { id :: DatabaseId, content :: a }

然后让一个
用户
类型没有
id
,让您的数据库功能与一个
有id的用户

一起工作,为什么您需要用户可打印?只需创建一个
showPrintable::User->String
函数。我怀疑您试图解决错误的问题。你想达到什么目标?如果您只是对不同情况下的字段子集感兴趣,那么只需传递整个类型。也许您需要将UserFromRequest替换为一个根据密码计算哈希的函数。顺便说一句,你有没有考虑过盐会去哪里?