在haskell中记录具有多个构造函数的类型

uv.*_*ita 12 haskell

通常,当我使用Haskell编写内容时,我需要具有多个构造函数的记录.例如,我想开发某种逻辑方案建模.我想到了这样的类型:

data Block a = Binary {
      binOp  :: a -> a -> a
    , opName :: String
    , in1 :: String
    , in2 :: String
    , out :: String
} | Unary {
      unOp  :: a -> a
    , opName :: String
    , in_ :: String
    , out :: String
}
Run Code Online (Sandbox Code Playgroud)

它描述了两种类型的块:二进制(如和,或等)和一元(如不是).它们包含核心功能,输入和输出信号.

另一个例子:类型来描述控制台命令.

data Command = Command { info :: CommandInfo
                       , action :: Args -> Action () }
             | FileCommand { info :: CommandInfo
                           , fileAction :: F.File -> Args -> Action ()
                           , permissions :: F.Permissions}
Run Code Online (Sandbox Code Playgroud)

FileCommand需要额外的字段 - 所需权限及其操作接受文件作为第一个参数.

当我阅读和搜索关于Haskell的主题,书籍等时,似乎同时使用具有记录语法和许多构造函数的类型并不常见.

所以问题是:这种"模式"是不是有问题,为什么?如果是这样,如何避免呢?

PS哪个来自提议的布局更好,或者可能有更多可读的?因为我在其他来源中找不到任何示例和建议.

Nik*_*kov 11

当事情开始变得复杂,分裂和征服.通过从较简单的组合创建复杂实体,而不是通过将所有功能组合到一个位置来创建复杂实体.事实证明,这是一般编程的最佳方法,而不仅仅是在Haskell中.

您的示例都可以从分离中受益.例如

data Block a = BinaryBlock (Binary a) | UnaryBlock (Unary a)

data Binary a = Binary {
  ...
}
data Unary = Unary {
  ...
}
Run Code Online (Sandbox Code Playgroud)

现在你有BinaryUnary分离,你能够孤立地写专用功能为他们每个人.这些功能将更简单,更容易推理和维护.

您还可以将这些类型放在单独的模块中,这将解决字段名称冲突.对于最终的API Block将有约非常简单的模式匹配,并转发给专门的职能BinaryUnary.

这种方法是可扩展的.无论您的实体或问题有多复杂,您都可以随意添加其他级别的分解.

  • 对于许多情况来说,这肯定是很好的建议,但我不完全同意_all_多块类型应该被拆分.当没有任何组成块本身有任何用途时,这主要是额外的类型名称空间污染而没有真正的好处. (2认同)

lef*_*out 8

这种类型的一个问题是访问器功能不再是完全的,这在当天是相当不赞成的,这是有充分理由的.这可能就是他们在书中避免的原因.

IMO,多构造函数记录原则上仍然很好,只需要理解标签应该用作访问器函数.但它们仍然非常有用,特别是RecordWildCards扩展.

在许多图书馆中都可以找到这种类型.当构造者被隐藏时,你绝对是好的.