如何在 PureScript 中组合记录类型的行? (在 PureScript 0.12.0 中是否有 Union 类型类的替代方案?)
How to combine rows of record types in PureScript? (Is there any alternative to the Union typeclass in PureScript 0.12.0?)
问题:我有不同的记录类型,其中有许多公共字段。我如何 "include" 记录类型定义中的公共字段?
示例:
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 中提到的 class Union
类型可能是我要找的...但它似乎从 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 )
.
您也可以使用 "pipe" 语法来扩展行:
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 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 中做的是更喜欢 "naked" 记录(如我上面示例中的 Record1
和 Record2
)并且只在你真正需要时才将它们包装在 newtype
中不得不。
费奥多尔的回答是正确的。但是,如果需要,还有另一种简洁的语法可以组合多种行类型。
通常,如果您有许多要合并的记录类型,您会这样做:
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
问题:我有不同的记录类型,其中有许多公共字段。我如何 "include" 记录类型定义中的公共字段?
示例:
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 中提到的 class Union
类型可能是我要找的...但它似乎从 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 )
.
您也可以使用 "pipe" 语法来扩展行:
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 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 中做的是更喜欢 "naked" 记录(如我上面示例中的 Record1
和 Record2
)并且只在你真正需要时才将它们包装在 newtype
中不得不。
费奥多尔的回答是正确的。但是,如果需要,还有另一种简洁的语法可以组合多种行类型。
通常,如果您有许多要合并的记录类型,您会这样做:
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