【问题标题】:Are extensible records useless in Elm 0.19?可扩展记录在 Elm 0.19 中没有用吗?
【发布时间】:2019-04-06 16:52:02
【问题描述】:

可扩展记录是 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

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

最后,经过这么长的介绍,让我陈述我的问题:

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

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

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

    type Named t = Named String t
    

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

【问题讨论】:

标签: elm


【解决方案1】:

类型{ 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 本身承认可扩展性目前不是很有用。

【讨论】:

    【解决方案2】:

    您可以只用name 包装t 类型,但与使用自定义类型的方法相比,它不会产生很大的不同:

    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
    

    【讨论】:

      【解决方案3】:

      是否存在积极的用例,即可扩展记录发挥关键作用的场景?

      是的,当您的应用程序Model 变得太大并且您面临如何扩展应用程序的问题时,它们很有用。可扩展记录允许您以任意方式对模型进行切片,而无需长期承诺特定切片。如果您通过将其拆分为几个较小的嵌套记录来将其分割,您将致力于这种特定的安排——这可能会导致嵌套的 TEA 和“输出消息”模式;通常是一个糟糕的设计选择。

      相反,使用可扩展记录来描述模型的切片,并将对特定切片进行操作的函数分组到它们自己的模块中。如果您以后需要跨模型的不同区域工作,您可以为此创建一个新的可扩展记录。

      Richard Feldman 在他的 Scaling Elm Apps 演讲中对其进行了描述:

      https://www.youtube.com/watch?v=DoA4Txr4GUs&ab_channel=ElmEurope

      我同意可扩展记录在 Elm 中似乎有点无用,但它们以最好的方式解决扩展问题是一件非常好的事情。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2023-04-02
        • 2014-01-06
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多