背景
我正在使用 Aeson 库来存储和检索文件中的值。我正在使用 Typeable
(和 TypeRep
)来标记数据,因此我很清楚它将正确解析。
我有一个类,其中类的每个成员都有一个需要时间(整数
)并更新自身的函数,即:
class Update a where
update :: Integer -> a -> a
文件中存储的所有值都是 Update
类的实例。
问题
我想迭代文件更新
每个值并写入一个新文件。我想让程序在运行时确定数据 block 的数据类型是什么,使用 fromJSON 创建该类型的值,对其运行 update ,然后写回来。类型检查器认为这是一个糟糕的想法,因为它无法在编译时对 fromJSON 调用进行静态类型检查,因此无法从 Update
类字典中获取正确的条目。
有没有办法使用 Typeable(或 Data)让类型检查器做正确的事情?有更好的选择吗?
<小时/>解决这个问题的唯一想法是创建所有可更新类型的联合数据类型,然后使用 case 语句来解析标记并选择正确的构造函数。我对这个解决方案不太满意,因为我无法在不接触这个联合类型的情况下向更新类添加新类型。
最佳答案
(这更像是一个扩展评论,而不是完整的答案。)
I'm not real happy with this solution because I cannot add new types to the update class without also touching this union type.
我建议实际上重新考虑这个推理。 Haskell 中的类型类表示某种类型具有特定的属性。例如,可以将这些数字相加。这并不意味着程序应该/想要使用这样的属性,该属性是数据类型固有的。而且它们是开放的 - 程序的不同部分可以添加新实例,并且程序的行为不应依赖于独立模块中声明的实例。
您的建议,即自动解析 Update
的所有实例,与上述意义背道而驰。它可能会降低你的程序的可读性,更重要的是,会在将来产生更多问题。如果有人想确定解析部分到底是如何工作的,他必须检查程序的完整源代码并查找 Update
的所有实例。我宁愿提倡局部性 - 程序的含义应该取决于它的“局部环境”。
它也不允许有您不想使用的实例(例如仅用于测试)。它还不允许您拥有 2 个或更多解析例程,每个例程使用一组不同的 Update
实例。
使用 Typeable
标记数据也可能存在问题,因为它不允许您轻松重构代码。如果您需要更改类型,您将无法解析数据。
所以我的建议是拥有这样的联合数据类型,但要简化相应的样板代码。
Aeson 已经让您能够使用 Generic
派生实例。因此您无需担心 FromJSON
/ToJSON
。您可以使用 Template Haskell 或使用 Generic 为该联合类型编写一次泛型 Upgrade
实例,这样无论联合类型的构造函数数量如何,它都会自动派生。这将使您能够对序列化/反序列化过程进行完全的本地控制,而在修改实例时只需很少的手动工作。
如果您愿意,我还可以举一个如何编写这样的实例的示例。
关于haskell - 在 haskell 中运行时反序列化为不同类型,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/45645748/