假设我有一个很长的数据结构定义
data A = A {
x1 :: String
, x2 :: String
...
, x50 :: String
}
现在我有3个任务:
这三项任务涉及标签x1,...,x50的繁琐复制。 更好的解决方案是通用列表
[
Foo "x1" aValue1
, Foo "x2" aValue2
...
]
因为它会使遍历和创建草稿变得更容易(列表定义已经是草案)。缺点是将其他数据结构映射到此处会更加危险,因为您将丢失静态类型检查。
这有意义吗? 是否存在通用但安全的解决方案?
编辑:为了让您更好地了解,它是将业务数据映射到文本表示形式,如表单和字母。 E.g:
data TaxData = TaxData {
taxId :: String
, income :: Money
, taxPayed :: Money,
, isMarried :: Bool
...
}
data TaxFormA = TaxFormA {
taxId :: Text
, isMarried :: Text
...
}
data TaxFormB = TaxFormB {
taxId :: Text
, taxPayedRounded :: Text
...
}
那些转化为文本流,代表实际的形式。如果我将在一次通过中创建一个来自税收数据的表格,并且明年任何表格字段都会移动,那么例如是一个迷路的“0.0”,我不知道它属于哪里。这就是中间数据结构的用途:它可以很容易地创建草稿数据。
所以我需要将实际的TaxData映射到那些中间表格数据;我需要将这些表单数据映射到实际的表单文本表示;我需要创建草稿中间表格数据。一方面我讨厌重复这些数据标签,另一方面它给了我安全,我不会在映射时混淆任何标签。是否有银弹?
答案 0 :(得分:3)
这样的深度结构化数据在Haskell中最常用的表示为嵌套的代数数据类型,就像你所做的那样。为什么?它为数据提供了最多的类型结构和安全性,防止功能将数据放入错误的格式。通过对某些类型进行newtyping可以获得更高的安全性,从而增加每个字段中数据之间的差异。
然而,像这样的非常大的ADT可能难以命名和操纵。例如,编译器设计的一个常见情况是指定如此大的ADT,并且为了帮助编写编译器的代码,我们倾向于使用许多通用编程技巧:SYB,元编程,甚至模板Haskell,以生成所有我们需要的样板。
因此,总而言之,我会保留您正在采用的ADT方法,但请查看使用泛型(例如SYB或Template Haskell)来生成一些定义和辅助函数。