可扩展记录是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
这将是非常重要的功能,因为我有很多例程可以接受老式的未命名事物,并且我需要一种将它们用于命名事物的方法。当然,大规模重构这些功能是不合格的解决方案。
最后,经过漫长的介绍,让我说一下我的问题:
现代Elm是否可以替代旧的过时字段添加/删除语法?
如果没有,上面是否有一些内置功能,例如equip
和extract
?对于每种自定义可扩展记录类型,我都希望有一个多态分析器(一个提取其基本部分的函数)和一个多态构造函数(一个将基本部分与加法运算相结合并生成记录的函数)。
(1)和(2)的否定答案将迫使我以更传统的方式实现Named t
:
type Named t = Named String t
在这种情况下,我无法达到可扩展记录的目的。是否有一个积极的用例,在这种情况下可扩展记录起着关键作用?
答案 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