结合Purescript中的记录

Tim*_*ter 8 purescript

鉴于我在purescript中有以下记录:

let name = {name: "Jim"}
let age = {age: 37}
Run Code Online (Sandbox Code Playgroud)

是否可以将这两个记录组合成一般的方式?就像是:

name 'comb' age
Run Code Online (Sandbox Code Playgroud)

这样我得到以下记录:

{name: "Jim", age: 37}
Run Code Online (Sandbox Code Playgroud)

不知怎的,似乎有可能使用Eff rowtype,但我很好奇是否可以使用'normal'记录.我是purescript的新手,也是它的记录语法.

非常感谢.

pal*_*luh 10

编辑:

似乎是目前官方包处理记录操作是purescript-record-你可以找到Builder.purs有提供mergebuild功能:

> import Data.Record.Builder (build, merge)
> name = {name: "Jim"}
> age = {age: 37}
> :t (build (merge age) name)
{ name :: String
, age :: Int
}
Run Code Online (Sandbox Code Playgroud)

API注意:

这个API乍一看看起来过于复杂 - 特别是当你将它与简单unionMerge name age调用进行比较时(unionMerge在这个答案的最后引入).Builder存在(以及此API)背后的原因是性能.我可以向你保证:

> build (merge name >>> merge age) {email: "someone@example.com"}
Run Code Online (Sandbox Code Playgroud)

只创建一条新记录.但是这个:

> unionMerge name (unionMerge age {email: "someone@example.com"})
Run Code Online (Sandbox Code Playgroud)

在执行期间创建两条记录.

什么是更有趣的是如何Builder,build以及merge实现- Builder是NEWTYPE包裹一个功能(其组成仅仅是一个函数组成)和build仅仅是对记录的复制版本功能应用:

newtype Builder a b = Builder (a -> b)

build (Builder b) r1 = b (copyRecord r1)
Run Code Online (Sandbox Code Playgroud)

merge那里unsafeMerge执行:

merge r2 = Builder \r1 -> unsafeMerge r1 r2
Run Code Online (Sandbox Code Playgroud)

那么我们为什么要在这里获得任何东西?因为我们可以确定中间结果不能转义函数范围,并且每个值在构建器链中只消耗一次.因此,我们可以以可变的方式"就地"执行所有转换.换句话说,这个intermediate值:

> intermediate = unionMerge name {email: "someone@example.com"}
> unionMerge age intermediate
Run Code Online (Sandbox Code Playgroud)

不能从这里"提取":

> build (merge name >>> merge age) {email: "someone@example.com"}
Run Code Online (Sandbox Code Playgroud)

并且它只被下一个构建器消耗一次,即merge age.

TYPESYSTEM评论:

看来Purescript类型系统可以处理这个,这要归功于以下Union类型类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.
Run Code Online (Sandbox Code Playgroud)

哪个有"魔法类型"(来源:幻灯片23):

Union r1 r2 r3 | r1 r2 -> r3, r1 r3 -> r2
Run Code Online (Sandbox Code Playgroud)

旧方法(仍然有效,但不是首选):

purescript-records包暴露出unionMerge你想要的东西(在我们不必使用的新psci中let):

> import Data.Record (unionMerge)
> name = {name: "Jim"}
> age = {age: 37}
> :t (unionMerge age name)
{ name :: String
, age :: Int
}
Run Code Online (Sandbox Code Playgroud)


gb.*_*gb. 5

目前不可能这样做,因为我们没有办法说一行缺少某些标签或其他标签.可以有一个开放记录类型:

something :: forall r. { name :: String | r } -> ...
Run Code Online (Sandbox Code Playgroud)

但这只允许我们接受带有name任何其他标签的记录,如果我们想要合并,扩展或减去记录,它对我们没有帮助.

组合任意记录的问题是我们有这样的类型签名:

comb :: forall r1 r2. { | r1 } -> { | r2 } -> ???
Run Code Online (Sandbox Code Playgroud)

我们需要某种方式来说结果(???)是r1和的结合r2,但我们也许想要说r1标签不与r2's 重叠.

将来,这可能通过行约束来实现.