【问题标题】:Record syntax and sum types记录语法和求和类型
【发布时间】:2016-06-06 07:57:37
【问题描述】:

我对 Haskell 中的 sum 类型有疑问。

我想创建一个 sum 类型,它由两个或多个其他类型组成,并且每个类型都可能包含多个字段。一个简单的例子是这样的:

data T3 = T1 { a :: Int, b :: Float} | T2 { x :: Char } deriving (Show)

在我的理解中,T1T2 是使用记录语法的数据构造函数。似乎T3 的定义会随着T1T2 中字段数量的增加而增加。我的问题是,如果字段数量很大,如何实际处理这些 sum 类型的构造函数?或者,将 sum 类型与记录语法混合使用是个好主意吗?

【问题讨论】:

  • 我不确定您真正想要什么:如果您有大量字段,您仍然必须将它们全部写入......您会怎么做?此外,字段和总和类型不能很好地结合在一起,因为例如a :: T3 -> Int 是部分的,一旦您错误地将 T2 'c' 值提供给它,您的程序就会崩溃。 (OTOH,如果字段仅用于模式,那么它们是安全的。)
  • “如何处理”:您遇到了什么实际问题?定义看起来太大了?你必须重复自己太多?记录不可扩展?还有什么?
  • ...避免将所有数据构造函数写在一行上?
  • 对不起,我的问题不是很清楚。我担心的是,由于 T1 和 T2 嵌入在 T3 中,如果 T1 或 T2 很复杂,T3 可能会变得臃肿。我正在寻找一种将 T1 和 T2 与 T3 “分离”的方法,以便可以单独定义 T1 和 T2 的内部。 @leftaroundabout 在下面展示了一个带有T3_1T3_2 的解决方案。

标签: haskell types algebraic-data-types


【解决方案1】:

我不太明白您有什么顾虑,但要回答最后一行的问题:,将 sum 类型与记录语法混合并不是一个好主意。总的来说,记录仍然是 Haskell 语言的一个弱点。他们根本不能很好地处理范围。只要您只有一些具有不同唱片标签的单独类型,通常就可以了,但是一旦出现总和类型或名称冲突,它就会变得相当讨厌。

特别是,Haskell 允许您将 T1 构造函数的记录字段访问器用于 T3 类型的任何值 - print $ a (T2 'x') 将在没有警告的情况下编译,但在运行时给出相当难以预见的错误。

在您的示例中,幸运的是您可以轻松避免这种麻烦:

data T3 = T3_1 T1 | T3_2 T2
           deriving (Show)
data T1 = T1 { a :: Int
             , b :: Float}
           deriving (Show)
data T2 = T2 { x :: Char }
           deriving (Show)

现在,您可以编写的任何解构都将经过适当的类型检查以使其有意义。

这样一个有意义的、小的专门子类型的结构通常比单一的单一类型更好地处理,特别是如果你有许多真正只处理部分数据结构的函数.

另一方面,解开构造函数层会变得非常乏味,但幸运的是现在已经解决了这个问题:lens libraries 允许您非常整齐地组合访问器/修饰符。

谈到已解决的问题:Nikita Volkov 提出了一个非常好的概念,可以完全取代问题重重的记录语法。


嗯...实际上这些不是任何正确意义上的子类型,但你明白我的意思。

【讨论】:

    猜你喜欢
    • 2010-12-26
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2022-08-17
    • 2023-03-16
    • 1970-01-01
    相关资源
    最近更新 更多