背景

我正在使用 Aeson 库来存储和检索文件中的值。我正在使用 Typeable (和 TypeRep s)来标记数据,所以我有一个很好的想法,它会正确解析。

我有一个类,其中类的每个成员都有一个需要时间( Integer )并自我更新的函数,即:

class Update a where
  update :: Integer -> a -> a

文件中存储的所有值都是 Update 类的实例。

问题
我想遍历每个值的文件 update 并写入一个新文件。我想让程序在运行时确定数据块的数据类型是什么,使用 fromJSON 创建该类型的值,在其上运行 update 并将其写回。类型检查器认为这是一个糟糕的主意,因为它无法在编译时对 fromJSON 调用进行静态类型检查,因此无法从 Update 类字典中获取正确的条目。

有没有办法使用 Typeable(或 Data)来让类型检查器做正确的事情?有更好的选择吗?

我解决这个问题的唯一想法是创建所有可更新类型的联合数据类型,然后使用 case 语句来解析标记并选择正确的构造函数。我对这个解决方案并不满意,因为我无法在不触及此联合类型的情况下向更新类添加新类型。

最佳答案

(这与其说是完整答案,不如说是扩展评论。)



我建议实际上重新考虑这个推理。 Haskell 中的类型类表示某种类型具有特定的属性。例如,可以添加这些数字。这并不意味着程序应该/想要使用这样的属性,该属性是数据类型固有的。而且它们是开放的——程序的不同部分可以添加新实例,并且程序的行为不应该依赖于在独立模块中声明的实例。

您的建议,即自动解析 Update 的所有实例,与上述意义背道而驰。它会使您的程序可读性降低,更重要的是,将来会产生更多问题。如果有人想确定解析部分的工作原理,他必须检查程序的完整源代码并查找 Update 的所有实例。我宁愿提倡局部性——一个程序的意义应该取决于它的“本地环境”。

它也不允许拥有您不想使用的实例(例如仅用于测试)。它也不允许您有 2 个或更多解析例程,每个解析例程都使用不同的 Update 实例集。

使用 Typeable 标记数据也可能有问题,因为它不允许您轻松重构代码。如果您需要更改您的类型,您将无法解析您的数据。

所以我的建议是拥有这样一个联合数据类型,但要简化相应的样板代码。

Aeson 已经为您提供了使用 Generic 派生实例的能力。所以你不需要担心 FromJSON/ToJSON 。您可以使用 Template Haskell 或使用 Generic 为该联合类型编写一次通用 Upgrade 实例,这样无论联合类型的构造函数数量如何,它都会自动派生。这将允许您对序列化/反序列化过程进行完全的本地控制,在修改实例时几乎不需要手动工作。

如果你愿意,我也可以举例说明如何编写这样的一个实例。

关于haskell - 在 Haskell 中在运行时反序列化为不同类型,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/45645748/

10-12 07:39