Haskell 我可以打部分记录吗?
我有一个带有姓名和id的Haskell 我可以打部分记录吗?,haskell,types,record,Haskell,Types,Record,我有一个带有姓名和id的Person记录,还有一个函数createPerson,它返回一个Person,不带id,将UUID的生成留给调用者: -- Person.hs import Data.UUID (UUID) data Person = Person { name :: String, id :: UUID } createPerson name = Person { name = name } 是否有一种方法可以在没有id的情况下键入人员,通知调用方id人员将引发异常?我考虑定义
Person
记录,还有一个函数createPerson
,它返回一个Person
,不带id
,将UUID的生成留给调用者:
-- Person.hs
import Data.UUID (UUID)
data Person = Person { name :: String, id :: UUID }
createPerson name = Person { name = name }
是否有一种方法可以在没有id的情况下键入人员
,通知调用方id人员
将引发异常?
我考虑定义PartialPerson
,如下所示:
data PartialPerson = { name :: String }
但当我想添加或更改字段时,这会很快变得很麻烦。第一种方法: 您可以使用
可能的UUID
定义人
:
data Person=Person{name::String,id::Maybe UUID}
现在,您可以定义一些有用的函数:
partialPerson::String->Person
partialPerson n=Person{name=n,id=Nothing}
getId::Person->UUID
getId(Person_uid)(只是uuid))=uuid
getId=错误“人员没有UUID”
但是getId
是不安全的函数,所以在使用它时必须小心
第二种方式:
您可以定义新的数据类型:
data PartialPerson=PartialPerson{name::String}
。。。并定义用于在这些类型之间转换的函数:
withId::PartialPerson->UUID->Person
withId(PartialPerson n)uuid=个人n uuid
withoutId::Person->PartialPerson
无ID(人员n uu)=部分人员n
对于没有身份证的人,一种可能的类型是:
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
第三种选择的问题是,将功能同时用于两种不同的人会变得更加麻烦
另外,从JSON自动派生的FromJSON
和ToJSON
实例可能会有不需要的嵌套
而且,当有多个可选属性时,它的可扩展性不是很强。将
Person
参数化以支持有id和无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 }
缺少id的记录的简单类型是
UUID->Person
。问题是你不能打印它或检查它的其他字段。也许你可以考虑。我不知道为什么你会定义<代码> GETId <代码> > <代码> ID::人->也许uuid >(忽略代码< > ID <代码>可能不是一个好的域名。@chepner为什么说id
不是一个好的字段名?因为id::a->a
已经是一个函数了。相关谈话(2016)还有一个选项:data Person f=Person{name::String,uuid::f uuid};键入PersonWithId=个人身份;键入PersonWithoutId=Person(Const())
。它看起来类似于您的第二个选项,但有一些。除了链接中提到的“rank2classes”库之外,另一个支持这种编程风格的库是“barbies”