由于我有如下因素记录purescript:
let name = {name: "Jim"}
let age = {age: 37}
是有可能的两个记录在一个通用的方式结合一些如何?就像是:
name 'comb' age
这样,我得到了如下的记录:
{name: "Jim", age: 37}
不知怎的,这似乎是可能的则Eff行类型,不过我很好奇,如果有可能与“正常”的记录。我是新来purescript和它的记录语法。
非常感谢。
这是不可能的时刻要做到这一点,因为我们没有说行缺少一些标签或其他的方式。它可以有一个开放的记录类型:
something :: forall r. { name :: String | r } -> ...
但是,这只能让我们接受name
和任何其他标签的记录,它并没有帮助我们,如果我们想结合,扩展,或从记录中减去,因为它主张。
相结合的任意记录的问题是,我们不得不这样的类型签名:
comb :: forall r1 r2. { | r1 } -> { | r2 } -> ???
我们需要一些方法来表示结果(???
)是r1
和r2
的结合,也是我们想也许想说的是,r1
的标签不会r2
的重叠。
在未来,这可以通过row constraints是可能的。
编辑:
它seems,目前官方包处理记录操作是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
在这个答案的末尾intoduced)。背后Builder
存在(所以这个API)的原因是性能。我可以向你保证,这个:
> build (merge name >>> merge age) {email: "[email protected]"}
只创建一个新的记录。但是这个:
> unionMerge name (unionMerge age {email: "[email protected]"})
在执行过程中创建两个记录。
什么是更有趣的是如何Builder
,build
和merge
实现 - Builder
是围绕功能NEWTYPE包装(其组成仅仅是一个函数组成)和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: "[email protected]"}
> unionMerge age intermediate
不能被“提取”从这里开始:
> build (merge name >>> merge age) {email: "[email protected]"}
它是由在未来的建设者,即merge age
只能使用一次。
类型系统评论:
看来,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.
具有这种“魔法类”(来源: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
}