我可以输入部分记录吗?

Nie*_*iek 11 haskell types record

我有一个Person带有名称和 ID的记录,以及一个createPerson返回Personwithout的函数,将id生成 UUID 留给调用者。:

-- Person.hs
import Data.UUID (UUID)

data Person = Person { name :: String, id :: UUID }

createPerson name = Person { name = name }
Run Code Online (Sandbox Code Playgroud)

有没有办法在Person没有id,的情况下输入 a来通知调用者id Person将抛出异常?我考虑过定义 aPartialPerson如下:

data PartialPerson = { name :: String }
Run Code Online (Sandbox Code Playgroud)

但是当我想添加或更改字段时,这很快就会变得很麻烦。

dan*_*iaz 10

没有身份证的人的一种可能类型是:

type PersonWithoutId = UUID -> Person
Run Code Online (Sandbox Code Playgroud)

问题是我们不能打印这种类型的值,或者检查它们的其他字段,因为函数是不透明的。


另一种选择是参数化类型:

data Person a = Person { name :: String, personId :: a }
type PersonWithId = Person UUID
type PersonWithoutId = Person ()
Run Code Online (Sandbox Code Playgroud)

这样做的好处是您仍然可以轻松派生出有用的类型类。


第三种选择是从个人中删除 id,并在需要时使用一对:

type PersonWithId = (UUID,Person)
Run Code Online (Sandbox Code Playgroud)

或使用专用类型:

data WithId a = WithId { theId :: UUID, theValue :: a } deriving Functor
Run Code Online (Sandbox Code Playgroud)

这第三个选项的问题在于,拥有同时适用于这两种人的功能会变得更加麻烦。

此外,自动派生FromJSONToJSON实例可能会有不希望的嵌套。

当有多个可选属性时,它不是很可扩展。

  • 多一个选项:`data Person f = Person { name :: String, uuid :: f UUID }; 类型 PersonWithId = 人员身份;类型 PersonWithoutId = Person (Const ())`。它看起来与您的第二个选项类似,但有一些[有吸引力的通用操作](https://www.benjamin.pizza/posts/2017-12-15-functor-functors.html)。 (5认同)

Ser*_*001 5

第一种方式:

您可以定义PersonMaybe UUID

data Person = Person { name :: String, id :: Maybe UUID }
Run Code Online (Sandbox Code Playgroud)

现在您可以定义一些有用的函数:

partialPerson :: String -> Person
partialPerson n = Person { name = n, id = Nothing }

getId :: Person -> UUID
getId (Person _ (Just uuid)) = uuid
getId _ = error "person hasn't UUID"
Run Code Online (Sandbox Code Playgroud)

但是getId是不安全的函数,所以使用时一定要小心。

第二种方式:

您可以定义新的数据类型:

data PartialPerson = PartialPerson { name :: String }
Run Code Online (Sandbox Code Playgroud)

...并定义这样的函数来在这些类型之间进行转换:

withId :: PartialPerson -> UUID -> Person
withId (PartialPerson n) uuid = Person n uuid

withoutId :: Person -> PartialPerson
withoutId (Person n _) = PartialPerson n
Run Code Online (Sandbox Code Playgroud)