可扩展记录是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
字段的记录,但是类型系统无法保证这一点。实际上,我尝试使用Named t
表示相反的内容:t
应该是没有自己的name
字段的记录类型,并且该函数将此字段添加到记录中。无论如何,要实现equip
函数,必须添加字段。因此,以多态方式编写
equip
似乎是不可能的,但是这可能没什么大不了的。毕竟,只要有时间我想给一些具体的事情起个名字,我都可以手工完成。更糟糕的是,逆函数extract : Named t -> t
(删除命名事物的名称)需要字段移除机制,因此也无法实现:extract : Named t -> t
extract thing = thing -- Error: No implicit upcast
这将是非常重要的功能,因为我有很多例程可以接受老式的未命名事物,并且我需要一种将它们用于命名事物的方法。当然,大量重构这些功能是不合格的解决方案。
最后,经过漫长的介绍之后,让我陈述一下我的问题:
equip
和extract
?对于每种自定义可扩展记录类型,我都希望有一个多态分析器(一个提取其基础部分的函数)和一个多态构造函数(一个将基础部分与加法运算相结合并生成记录的函数)。 Named t
:type Named t = Named String t
在这种情况下,我无法达到可扩展记录的目的。有没有积极的用例,在这种情况下可扩展记录起着至关重要的作用?
最佳答案
类型{ 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
投影,因此也许应该将其称为作用域记录。 Elm docs本身承认目前的可扩展性不是很有用。
关于elm - 在Elm 0.19中可扩展的记录是没有用的吗?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55551328/