Elm 0.19 中的可扩展记录没用吗?

con*_*yev 5 elm

可扩展记录是 Elm 最令人惊奇的功能之一,但自 v0.16 以来,添加和删除字段不再可用。这让我处于尴尬的境地。

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

type alias Named t = { t | name: String }
Run Code Online (Sandbox Code Playgroud)

“好的,”编译器说。现在我需要一个构造函数,即一个事物配备指定名称的函数:

equip : String -> t -> Named t
equip name thing = { thing | name = name }  -- Oops! Type mismatch
Run Code Online (Sandbox Code Playgroud)

编译失败,因为{ thing | name = ... }语法假定thing是带有name字段的记录,但类型系统不能保证这一点。事实上,Named t我试图表达一些相反的东西:t应该是一个没有自己name字段的记录类型,并且该函数将此字段添加到记录中。无论如何,字段添加是实现equip功能所必需的。

因此,equip以多态方式编写似乎是不可能的,但这可能不是什么大问题。毕竟,任何时候我要给一些具体的东西命名时,我都可以手动完成。更糟糕的是,逆函数extract : Named t -> t(擦除命名事物的名称)需要字段删除机制,因此也无法实现:

extract : Named t -> t
extract thing = thing  -- Error: No implicit upcast
Run Code Online (Sandbox Code Playgroud)

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

最后,在经过这么长的介绍之后,让我提出我的问题:

  1. 现代 Elm 是否提供了一些替代旧的已弃用字段添加/删除语法?

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

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

    type Named t = Named String t
    
    Run Code Online (Sandbox Code Playgroud)

    在这种情况下,我无法理解可扩展记录的目的。是否有积极的用例,可扩展记录在其中发挥关键作用的场景?

Jan*_*nar 7

类型{ t | name : String }表示具有name字段的记录。它不扩展t类型,而是扩展编译器关于t自身的知识。

所以实际上的类型equipString -> { t | name : String } -> { t | name : String }.

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

同样,不支持从记录中删除字段。

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

关于可扩展记录,Elm 不再真正支持“可扩展”部分——唯一剩下的部分是{ t | name : u } -> u投影,所以也许它应该被称为作用域记录。Elm文档本身承认可扩展性目前不是很有用。