【问题标题】:Can I type a partial record?我可以键入部分记录吗?
【发布时间】:2021-02-17 12:24:06
【问题描述】:

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

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

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

createPerson name = Person { name = name }

有没有办法在没有id 的情况下键入Person,以通知调用者id Person 将引发异常?我考虑过如下定义PartialPerson

data PartialPerson = { name :: String }

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

【问题讨论】:

  • 缺少 id 的记录的简单类型是 UUID -> Person。问题是您无法打印或检查其其他字段。
  • 或许你可以考虑using a parametric type for this

标签: haskell types record


【解决方案1】:

没有 id 的人的一种可能类型是:

type PersonWithoutId = UUID -> Person

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


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

data Person a = Person { name :: String, personId :: a }
type PersonWithId = Person UUID
type PersonWithoutId = Person ()

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


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

type PersonWithId = (UUID,Person)

或使用专用类型:

data WithId a = WithId { theId :: UUID, theValue :: a } deriving Functor

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

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

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

【讨论】:

【解决方案2】:

第一种方式:

你可以用Maybe UUID定义Person

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

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

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

getId :: Person -> UUID
getId (Person _ (Just uuid)) = uuid
getId _ = error "person hasn't UUID"

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

第二种方式:

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

data PartialPerson = PartialPerson { name :: String }

...并定义此类函数以在这些类型之间进行转换:

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

withoutId :: Person -> PartialPerson
withoutId (Person n _) = PartialPerson n

【讨论】:

  • 我不知道你为什么要在id :: Person -> Maybe UUID 已经存在时定义getId(忽略id 可能不是一个好的字段名称这一事实。)
  • @chepner 为什么说id 不是一个好字段名?
  • 因为id :: a -> a已经是一个函数了。
【解决方案3】:

使 Person 参数化以支持有和没有 id 的人的想法的变体,同时保持僵化 id 字段是关于 UUID,而不仅仅是一些任意的参数类型:

{-# LANGUAGE DataKinds, KindSignatures, GADTs #-}

data IdRequirement
  = NoIdNeeded | IdOptional | IdRequired

data IdField (reqId :: IdRequirement) where
  IdLess :: IdField 'NoIdNeeded
  IdOmitted :: IdField 'IdOptional
  IdProvided :: UUID -> IdField 'IdOptional
  RequiredId ::  UUID -> IdField 'IdRequired

data Person (reqId :: IdRequirement)
  = Person { personName :: String
           , personId :: IdField reqId }

【讨论】:

    猜你喜欢
    • 2012-05-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2023-04-10
    • 1970-01-01
    • 1970-01-01
    • 2020-04-21
    相关资源
    最近更新 更多