可扩展记录在Elm 0.19中有用吗?

时间:2019-04-06 16:52:02

标签: elm

可扩展记录是Elm最令人惊奇的功能之一,但是自v0.16开始,添加和删除字段is no longer available。这使我处于尴尬的境地。

考虑一个例子。我想为随机事物t命名,而可扩展的记录为我提供了一个完美的工具:

type alias Named t = { t | name: String }

“好,” 说。现在我需要一个构造函数,即一个装备具有指定名称的东西的函数:

equip : String -> t -> Named t
equip name thing = { thing | name = name }  -- Oops! Type mismatch

编译失败,因为{ thing | name = ... }语法假定thing是具有name字段的记录,但是类型system无法保证这一点。实际上,我尝试使用Named t来表达相反的内容:t应该是记录类型而没有自己的name字段,并且该函数将其添加字段记录。无论如何,要实现equip功能,必须添加字段。

因此,以多态方式编写equip似乎是不可能的,但是这可能没什么大不了的。毕竟,只要有时间我想给一些具体的事情起个名字,我都可以手工完成。更糟糕的是,反函数extract : Named t -> t(删除命名事物的名称)需要字段移除机制,因此也无法实现:

extract : Named t -> t
extract thing = thing  -- Error: No implicit upcast

这将是非常重要的功能,因为我有很多例程可以接受老式的未命名事物,并且我需要一种将它们用于命名事物的方法。当然,大规模重构这些功能是不合格的解决方案。

最后,经过漫长的介绍,让我说一下我的问题:

  1. 现代Elm是否可以替代旧的过时字段添加/删除语法?

  2. 如果没有,上面是否有一些内置功能,例如equipextract?对于每种自定义可扩展记录类型,我都希望有一个多态分析器(一个提取其基本部分的函数)和一个多态构造函数(一个将基本部分与加法运算相结合并生成记录的函数)。

  3. (1)和(2)的否定答案将迫使我以更传统的方式实现Named t

    type Named t = Named String t
    

    在这种情况下,我无法达到可扩展记录的目的。是否有一个积极的用例,在这种情况下可扩展记录起着关键作用?

2 个答案:

答案 0 :(得分:3)

类型{ t | name : String }表示具有name字段的记录。它不会扩展t类型,而是扩展了编译器对t本身的了解。

因此,实际上equip的类型为String -> { t | name : String } -> { t | name : String }

此外,正如您所注意到的,Elm不再支持向记录中添加字段,因此即使类型系统允许了您想要的内容,您仍然无法做到这一点。 { thing | name = name }语法仅支持更新{ t | name : String }类型的记录。

类似地,不支持从记录中删除字段。

如果确实需要具有可以添加或删除字段的类型,则可以使用Dict。其他选择是手动编写转换器,或者创建并使用代码生成器(这是一段时间内JSON解码样板的推荐解决方案)。

关于可扩展记录,Elm不再真正支持“可扩展”部分–唯一剩下的部分是{ t | name : u } -> u投影,因此也许应该将其称为作用域记录。榆树docs本身承认,目前的可扩展性不是很有用。

答案 1 :(得分:2)

您可以将t类型与name进行包装,但是与使用自定义类型的方法相比,并没有太大的区别:

type alias Named t = { val: t, name: String }

equip : String -> t -> Named t
equip name thing = { val = thing, name = name }

extract : Named t -> t
extract thing = thing.val