鉴于我在purescript中有以下记录:
let name = {name: "Jim"}
let age = {age: 37}
是否可以将这两个记录组合成一般的方式? 类似的东西:
name 'comb' age
这样我得到以下记录:
{name: "Jim", age: 37}
似乎有可能使用Eff rowtype,但我很好奇是否可以使用'normal'记录。我是purescript的新手,也是它的记录语法。
非常感谢。
答案 0 :(得分:9)
编辑:
目前处理记录操作的官方包purescript-record
seems - 您可以找到提供merge
和build
功能的Builder.purs:< / p>
> 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
调用进行比较时({1}}在本回答结尾处被引入)。 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
那么我们为什么要在这里获得任何东西?因为我们可以确定中间结果不能逃避功能范围,并且每个值在构建器链中只消耗一次。因此,我们可以在适当的位置执行所有转换&#34;以可变的方式。换句话说,这个merge r2 = Builder \r1 -> unsafeMerge r1 r2
值:
intermediate
无法提取&#34;从这里:
> intermediate = unionMerge name {email: "someone@example.com"}
> unionMerge age intermediate
并且它只被下一个构建器消耗一次,即> build (merge name >>> merge age) {email: "someone@example.com"}
。
TYPESYSTEM评论:
由于来自merge age
的{{1}}类型类,Purescript类型系统似乎可以处理此问题:
Union
这有什么&#34;魔法类型&#34; (来源:slide 23):
Prim
旧方法(仍然有效,但不是首选):
有purescript-records个软件包公开了unionMerge
,它完全符合您的要求(在新的psci中,我们不必使用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.
):
Union r1 r2 r3 | r1 r2 -> r3, r1 r3 -> r2
答案 1 :(得分:5)
目前不可能这样做,因为我们没有办法说一行缺少某些标签或其他标签。可以有一个开放记录类型:
something :: forall r. { name :: String | r } -> ...
但这只允许我们接受带有name
和任何其他标签的记录,如果我们想要合并,扩展或减去记录,它对我们没有帮助。
组合任意记录的问题是我们有这样的类型签名:
comb :: forall r1 r2. { | r1 } -> { | r2 } -> ???
我们需要某种方式来说结果(???
)是r1
和r2
的联合,但我们也许想说r1
' s标签不与r2
重叠。
将来可以通过row constraints来实现。