问题:我有许多常见字段的不同记录类型。我如何“包含”记录类型定义中的公共字段?
例:
newtype RecordType1 = RecordType1 { a :: Int, b :: Int, y :: String }
newtype RecordType2 = RecordType2 { a :: Int, b :: Int, z :: Boolean }
如何在PureScript中编写等效的?
newtype RecordType1 = RecordType1 { CommonFields, y :: String }
newtype RecordType2 = RecordType2 { CommonFields, z :: Boolean }
在Union
中提到的类型类An Overview of the PureScript Type System可能是我想要的...但它似乎是自PureScript 0.12.0以来。
有什么建议?有什么我想念的吗?
谢谢!
PureScript具有用于组合行的特殊语法:
type Common = ( a :: Int, b :: Int )
type Record1 = { y :: String | Common }
type Record2 = { z :: Boolean | Common }
newtype RecordType3 = RecordType3 { w :: Number | Common }
请注意,Common
的定义使用括号,而不是花括号。那是因为Common
是一排,而不是记录。你可以创建一个记录:
type CommonRec = Record Common
-- equivalent to: CommonRec = { a :: Int, b :: Int }
实际上,花括号符号只是将Record
应用于行的语法糖。一个表达{ xyz }
去了Record ( xyz )
desugared。
您也可以使用“管道”语法来扩展行:
type CommonPlusFoo = ( foo :: Bar | Common )
type RecWithFoo = { x :: Int | CommonPlusFoo }
您还可以通过提供Common
作为类型参数来使记录类型具有多态性:
type Record1Poly r = { y :: String | r }
type Record1 = Record1Poly Common
这对于编写使用部分记录的函数非常方便,例如:
updateName :: forall r. { name :: String | r } -> { name :: String | r }
updateName x = x { name = "Mr. " <> x.name }
jones = { name: "Jones", occupation: "Plumber" }
mrJones = updateName jones -- mrJones = { name: "Mr. Jones", occupation: "Plumber" }
在此示例中,该函数可以与任何具有name
字段的记录一起使用,无论它可能具有什么。
最后,要表示一个空行,请使用空的parens:
type Record1Poly r = { y :: String | r }
type Record1 = Record1Poly Common
type OnlyY = Record1Poly ()
在一个稍微不相关的主题上,请注意PureScript中的记录与Haskell中的记录不同。例如,上面的Record1
和Record2
是真正的PureScript ad-hoc可扩展记录(Haskell没有的东西),但RecordType3
是一个新类型,它有一个构造函数,其参数是一个记录。
一个重要的区别是,与Haskell不同,这不起作用:
x = RecordType3 { w: 42.0, a: 1, b: 2 }
y = w x
表达式w x
(甚至表达式x.w
)不能编译,因为RecordType3
本身不是一个记录,它是一个包装记录的新类型。为了从中获取w
,您需要首先匹配构造函数:
(RecordType3 k) = x
y = k.w
或者将其作为访问者函数包装:
unRecordType3 (RecordType3 k) = k
y = (unRecordType3 x).w
实际上,如果您正在接近具有Haskell思维模式的记录,这真的很不方便。相反,你想要在PureScript中做什么更喜欢“裸”记录(如上面的例子中的Record1
和Record2
),并且只在你需要的时候将它们包装在newtype
中。