如何在PureScript中组合记录类型的行?(PureScript 0.12.0中是否有Union类型类的替代方法?)

Mat*_*ois 3 union records rows purescript

问题:我有许多常见字段的不同记录类型.我如何"包含"记录类型定义中的公共字段?

例:

newtype RecordType1 = RecordType1 { a :: Int, b :: Int, y :: String }
newtype RecordType2 = RecordType2 { a :: Int, b :: Int, z :: Boolean } 
Run Code Online (Sandbox Code Playgroud)

如何在PureScript中编写等效的?

newtype RecordType1 = RecordType1 { CommonFields, y :: String }
newtype RecordType2 = RecordType2 { CommonFields, z :: Boolean }
Run Code Online (Sandbox Code Playgroud)

" PureScript类型系统概述"中Union提到的类型类可能是我所寻求的......但它似乎是自PureScript 0.12.0以来的.

有什么建议?有什么我想念的吗?

谢谢!

Fyo*_*kin 15

PureScript具有用于组合行的特殊语法:

type Common = ( a :: Int, b :: Int )
type Record1 = { y :: String | Common }
type Record2 = { z :: Boolean | Common }
newtype RecordType3 = RecordType3 { w :: Number | Common }
Run Code Online (Sandbox Code Playgroud)

请注意,Common使用括号的定义,而不是花括号.那是因为Common一行,而不是记录.你可以创建一个记录:

type CommonRec = Record Common 
-- equivalent to:  CommonRec = { a :: Int, b :: Int }
Run Code Online (Sandbox Code Playgroud)

实际上,花括号符号只是用于Record行的语法糖.表达{ xyz }变得卑鄙Record ( xyz ).

您也可以使用"管道"语法来扩展行:

type CommonPlusFoo = ( foo :: Bar | Common )
type RecWithFoo = { x :: Int | CommonPlusFoo }
Run Code Online (Sandbox Code Playgroud)

您还可以通过提供Common类型参数来使记录类型具有多态性:

type Record1Poly r = { y :: String | r }
type Record1 = Record1Poly Common
Run Code Online (Sandbox Code Playgroud)

这对于编写使用部分记录的函数非常方便,例如:

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" }
Run Code Online (Sandbox Code Playgroud)

在此示例中,该函数可以处理具有name字段的任何记录,而不管它可能具有什么.


最后,要表示一个空行,请使用空的parens:

type Record1Poly r = { y :: String | r }
type Record1 = Record1Poly Common
type OnlyY = Record1Poly ()
Run Code Online (Sandbox Code Playgroud)

在一个稍微不相关的主题上,请注意PureScript中的记录与Haskell中的记录不同.例如,上面Record1Record2是真正的PureScript特设扩展记录(的东西,Haskell没有),而且RecordType3是具有一个构造函数,其参数是一个记录一个NEWTYPE.

一个重要的区别是,与Haskell不同,这不起作用:

 x = RecordType3 { w: 42.0, a: 1, b: 2 }
 y = w x
Run Code Online (Sandbox Code Playgroud)

表达式w x(甚至表达式x.w)不会编译,因为RecordType3它本身不是记录,它是包装记录的新类型.为了w摆脱它,你需要首先匹配构造函数:

 (RecordType3 k) = x
 y = k.w
Run Code Online (Sandbox Code Playgroud)

或者将其作为访问者函数包装:

 unRecordType3 (RecordType3 k) = k
 y = (unRecordType3 x).w
Run Code Online (Sandbox Code Playgroud)

实际上,如果您正在接近具有Haskell思维模式的记录,这真的很不方便.相反,你想在PureScript做的是更喜欢"裸"的记录(如Record1Record2上面我的例子),只有诉诸于包装它们newtype当你真的不得不这样做.


小智 8

费奥多尔的回答是正确的。但是,如果需要,还有另一种简洁的语法可以组合许多行类型。

通常,如果您有许多要组合的记录类型,您可以这样做:

type Foo r = ( x :: String | r )
type Bar r = ( y :: Int | r )
type FooBar r = Foo (Bar r)
Run Code Online (Sandbox Code Playgroud)

但是如果你有多个组合,或者名称太长,这会变得很麻烦:

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))
Run Code Online (Sandbox Code Playgroud)

所以你可以使用一个很好的语法将它们组合到 Type 模块中:

import Type.Row (type (+))
type ThisIsAFooBarBaz r = ThisIsAFoo + ThisIsABar + ThisIsABaz + r
Run Code Online (Sandbox Code Playgroud)