鉴于我在 purescript 中有以下记录:
let name = {name: "Jim"}
let age = {age: 37}
是否有可能以一种通用的方式将这两个记录结合起来?就像是:
name 'comb' age
这样我得到以下记录:
{name: "Jim", age: 37}
不知何故,Eff 行类型似乎是可能的,但我很好奇“正常”记录是否可能。我是 purescript 的新手,它是记录语法。
非常感谢。
鉴于我在 purescript 中有以下记录:
let name = {name: "Jim"}
let age = {age: 37}
是否有可能以一种通用的方式将这两个记录结合起来?就像是:
name 'comb' age
这样我得到以下记录:
{name: "Jim", age: 37}
不知何故,Eff 行类型似乎是可能的,但我很好奇“正常”记录是否可能。我是 purescript 的新手,它是记录语法。
非常感谢。
编辑:
似乎目前处理记录操作的官方包是-purescript-record
你可以找到Builder.purs那里提供merge
和build
功能:
> import Data.Record.Builder (build, merge)
> name = {name: "Jim"}
> age = {age: 37}
> :t (build (merge age) name)
{ name :: String
, age :: Int
}
API 注释:
乍一看,此 API 看起来过于复杂 - 特别是当您将其与简单unionMerge name age
调用进行比较时(unionMerge
在此答案的末尾引入)。存在(以及这个 API)背后的原因Builder
是性能。我可以向你保证:
> build (merge name >>> merge age) {email: "someone@example.com"}
仅创建一个新记录。但是这个:
> unionMerge name (unionMerge age {email: "someone@example.com"})
在执行期间创建两条记录。
更有趣的是Builder
,build
和是如何merge
实现的 -Builder
是围绕一个函数的新类型包装器(它的组合只是一个函数组合)并且build
只是一个复制版本记录的函数应用程序:
newtype Builder a b = Builder (a -> b)
build (Builder b) r1 = b (copyRecord r1)
在merge
那里unsafeMerge
执行:
merge r2 = Builder \r1 -> unsafeMerge r1 r2
那么为什么我们在这里获得任何东西?因为我们可以确定中间结果不能逃逸函数作用域,并且每个值在构建器链中只使用一次。因此,我们可以以可变的方式“就地”执行所有转换。换句话说,这个intermediate
值:
> intermediate = unionMerge name {email: "someone@example.com"}
> unionMerge age intermediate
不能从这里“提取”:
> build (merge name >>> merge age) {email: "someone@example.com"}
并且它只被下一个构建器消耗一次,即merge age
.
类型系统评论:
Union
由于来自以下的类型类,Purescript 类型系统现在似乎可以处理这个问题Prim
:
The Union type class is used to compute the union of two rows
of types (left-biased, including duplicates).
The third type argument represents the union of the first two.
它具有这种“魔法类型”(来源:幻灯片 23):
Union r1 r2 r3 | r1 r2 -> r3, r1 r3 -> r2
旧方法(仍然有效但不是首选):
有一个purescript-records包,它暴露unionMerge
了你想要的(在新的 psci 中我们不必使用let
):
> import Data.Record (unionMerge)
> name = {name: "Jim"}
> age = {age: 37}
> :t (unionMerge age name)
{ name :: String
, age :: Int
}
注意:当这个答案被接受时,这是真的,但现在我们确实有它提到的行约束,以及一个用于处理包含合并/联合的记录的库:https ://github.com/purescript/purescript-record
目前不可能这样做,因为我们没有办法说一行缺少一些标签或其他标签。可以有一个打开的记录类型:
something :: forall r. { name :: String | r } -> ...
但这仅允许我们接受带有name
和任何其他标签的记录,如果我们想从现有的记录中组合、扩展或减去,它对我们没有帮助。
组合任意记录的问题是我们会有这样的类型签名:
comb :: forall r1 r2. { | r1 } -> { | r2 } -> ???
我们需要某种方式来说明结果 ( ???
) 是 and 的r1
并集r2
,但我们可能还想说r1
's 标签不与r2
's 重叠。
将来这可能通过行约束来实现。