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

时间:2014-12-18 18:18:15

标签: haskell types

我有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

1 个答案:

答案 0 :(得分:2)

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

userFromRequest :: Email -> InstitutionId -> String -> User

请注意,您还可以为EmailInstitutionId制作单独的类型,这可以帮助您避免一堆恼人的错误。这与将带有标记字段的记录作为参数的目的相同,同时还增加了一些额外的静态安全性。您可以将它们实现为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一起使用。