在 Purescript 中合并记录
Combine Records in Purescript
鉴于我有以下纯脚本记录:
let name = {name: "Jim"}
let age = {age: 37}
是否可以通过某种通用方式将这两个记录组合起来?
类似于:
name 'comb' age
这样我得到以下记录:
{name: "Jim", age: 37}
不知何故,Eff 行类型似乎是可行的,但我很好奇 'normal' 记录是否可行。我是 purescript 的新手,它是记录语法。
非常感谢。
注意:当这个答案被接受时,它是正确的,但现在我们确实有它提到的行约束,以及一个用于处理记录的库,其中包括 merges/unions: https://github.com/purescript/purescript-record
目前无法执行此操作,因为我们没有办法说明某行缺少某个标签或其他标签。可以有一个打开的记录类型:
something :: forall r. { name :: String | r } -> ...
但这只允许我们接受带有 name
和任何其他标签的记录,如果我们想按原样合并、扩展或减去记录,它对我们没有帮助。
合并任意记录的问题是我们会有这样的类型签名:
comb :: forall r1 r2. { | r1 } -> { | r2 } -> ???
我们需要某种方式来说明结果 (???
) 是 r1
和 r2
的并集,但我们也许还想说 r1
的标签不与 r2
的标签重叠。
将来可能会通过 row constraints。
编辑:
那里seems that currently the official package for handling record manipulations is purescript-record
- you can find 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
在这个答案的末尾被引入)。 Builder
存在(以及 API)背后的原因是性能。我可以向你保证:
> 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
那么为什么我们在这里有所收获??因为我们可以确定中间结果不能逃脱函数作用域,并且每个值在构建器链中只被消耗一次。因此,我们可以以可变方式执行所有转换 "in place"。换句话说,这个 intermediate
值:
> intermediate = unionMerge name {email: "someone@example.com"}
> unionMerge age intermediate
不能从这里 "extracted":
> build (merge name >>> merge age) {email: "someone@example.com"}
并且它只会被下一个建造者消耗一次,即merge age
。
类型系统注释:
由于 Prim
:
中的 Union
类型 class,Purescript 类型系统现在似乎可以处理这个问题
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.
哪个有这个"magic type"(来源:slide 23):
Union r1 r2 r3 | r1 r2 -> r3, r1 r3 -> r2
旧方法(仍然有效但不是首选):
purescript-records package which exposes unionMerge
完全符合您的要求(在新的 psci 中我们不必使用 let
):
> import Data.Record (unionMerge)
> name = {name: "Jim"}
> age = {age: 37}
> :t (unionMerge age name)
{ name :: String
, age :: Int
}
鉴于我有以下纯脚本记录:
let name = {name: "Jim"}
let age = {age: 37}
是否可以通过某种通用方式将这两个记录组合起来? 类似于:
name 'comb' age
这样我得到以下记录:
{name: "Jim", age: 37}
不知何故,Eff 行类型似乎是可行的,但我很好奇 'normal' 记录是否可行。我是 purescript 的新手,它是记录语法。
非常感谢。
注意:当这个答案被接受时,它是正确的,但现在我们确实有它提到的行约束,以及一个用于处理记录的库,其中包括 merges/unions: https://github.com/purescript/purescript-record
目前无法执行此操作,因为我们没有办法说明某行缺少某个标签或其他标签。可以有一个打开的记录类型:
something :: forall r. { name :: String | r } -> ...
但这只允许我们接受带有 name
和任何其他标签的记录,如果我们想按原样合并、扩展或减去记录,它对我们没有帮助。
合并任意记录的问题是我们会有这样的类型签名:
comb :: forall r1 r2. { | r1 } -> { | r2 } -> ???
我们需要某种方式来说明结果 (???
) 是 r1
和 r2
的并集,但我们也许还想说 r1
的标签不与 r2
的标签重叠。
将来可能会通过 row constraints。
编辑:
那里seems that currently the official package for handling record manipulations is purescript-record
- you can find 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
在这个答案的末尾被引入)。 Builder
存在(以及 API)背后的原因是性能。我可以向你保证:
> 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
那么为什么我们在这里有所收获??因为我们可以确定中间结果不能逃脱函数作用域,并且每个值在构建器链中只被消耗一次。因此,我们可以以可变方式执行所有转换 "in place"。换句话说,这个 intermediate
值:
> intermediate = unionMerge name {email: "someone@example.com"}
> unionMerge age intermediate
不能从这里 "extracted":
> build (merge name >>> merge age) {email: "someone@example.com"}
并且它只会被下一个建造者消耗一次,即merge age
。
类型系统注释:
由于 Prim
:
Union
类型 class,Purescript 类型系统现在似乎可以处理这个问题
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.
哪个有这个"magic type"(来源:slide 23):
Union r1 r2 r3 | r1 r2 -> r3, r1 r3 -> r2
旧方法(仍然有效但不是首选):
purescript-records package which exposes unionMerge
完全符合您的要求(在新的 psci 中我们不必使用 let
):
> import Data.Record (unionMerge)
> name = {name: "Jim"}
> age = {age: 37}
> :t (unionMerge age name)
{ name :: String
, age :: Int
}