结合记录Purescript

问题描述 投票:6回答:2

由于我有如下因素记录purescript:

let name = {name: "Jim"}
let age = {age: 37}

是有可能的两个记录在一个通用的方式结合一些如何?就像是:

name 'comb' age

这样,我得到了如下的记录:

{name: "Jim", age: 37}

不知怎的,这似乎是可能的则Eff行类型,不过我很好奇,如果有可能与“正常”的记录。我是新来purescript和它的记录语法。

非常感谢。

purescript
2个回答
5
投票

这是不可能的时刻要做到这一点,因为我们没有说行缺少一些标签或其他的方式。它可以有一个开放的记录类型:

something :: forall r. { name :: String | r } -> ...

但是,这只能让我们接受name和任何其他标签的记录,它并没有帮助我们,如果我们想结合,扩展,或从记录中减去,因为它主张。

相结合的任意记录的问题是,我们不得不这样的类型签名:

comb :: forall r1 r2. { | r1 } -> { | r2 } -> ???

我们需要一些方法来表示结果(???)是r1r2的结合,也是我们想也许想说的是,r1的标签不会r2的重叠。

在未来,这可以通过row constraints是可能的。


10
投票

编辑:

seems,目前官方包处理记录操作是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
}

API注意:

这个API乍看上去过于复杂的 - 尤其是当你把它比作简单unionMerge name age调用(unionMerge在这个答案的末尾intoduced)。背后Builder存在(所以这个API)的原因是性能。我可以向你保证,这个:

> build (merge name >>> merge age) {email: "[email protected]"}

只创建一个新的记录。但是这个:

> unionMerge name (unionMerge age {email: "[email protected]"})

在执行过程中创建两个记录。

什么是更有趣的是如何Builderbuildmerge实现 - Builder是围绕功能NEWTYPE包装(其组成仅仅是一个函数组成)和build只是在记录的复制版本功能应用:

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

build (Builder b) r1 = b (copyRecord r1)

mergeunsafeMerge进行:

merge r2 = Builder \r1 -> unsafeMerge r1 r2

那么,为什么我们这里获得什么?因为我们可以肯定的是中间结果无法逃避的功能范围,并且每个值在建设者链消耗一次。因此,我们可以在一个可变的方式执行“到位”的所有转换。换句话说,这intermediate值:

> intermediate = unionMerge name {email: "[email protected]"}
> unionMerge age intermediate

不能被“提取”从这里开始:

> build (merge name >>> merge age) {email: "[email protected]"}

它是由在未来的建设者,即merge age只能使用一次。

类型系统评论:

看来,Purescript类型的系统可以处理这个问题,现在多亏了UnionPrim类型类:

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.

具有这种“魔法类”(来源:slide 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
}
© www.soinside.com 2019 - 2024. All rights reserved.