问题:我有许多常见字段的记录类型不同。如何在记录类型定义中“包括”公用字段?
示例:
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 }
An Overview of the PureScript Type System中提到的类型类Union
可能正是我想要的...但是自PureScript 0.12.0起似乎已经淘汰了。
有什么建议吗?我有什么想念的吗?
谢谢!
答案 0 :(得分:9)
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 )
。
您也可以使用“管道”语法来扩展行:
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
字段的记录,无论它可能还有什么。
最后,要表达一个空行,请使用空括号:
type Record1Poly r = { y :: String | r }
type Record1 = Record1Poly Common
type OnlyY = Record1Poly ()
在一个稍微不相关的主题上,请注意,PureScript中的记录与Haskell中的记录不同。例如,在Record1
和Record2
之上是真正的PureScript即席可扩展记录(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
中
答案 1 :(得分:4)
费奥多的答案是正确的。但是,如果需要,还有另一种干净的语法可用于组合许多行类型。
通常,如果要合并的记录类型很多,则可以执行以下操作:
type Foo r = ( x :: String | r )
type Bar r = ( y :: Int | r )
type FooBar r = Foo (Bar r)
但是如果要合并的名称不止一个,或者名称太长,这将变得很麻烦:
type ThisIsAFoo r = ( x :: String | r )
type ThisIsABar r = ( y :: Int | r )
type ThisIsABaz r = ( z :: Number | r )
type ThisIsAFooBarBaz r = ThisIsAFoo (ThisIsABar (ThisIsABaz r))
因此您可以在Type模块中使用一种不错的语法来组合它们:
import Type.Row (type (+))
type ThisIsAFooBarBaz r = ThisIsAFoo + ThisIsABar + ThisIsABaz + r