通常,当我使用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)
现在你有Binary和Unary分离,你能够孤立地写专用功能为他们每个人.这些功能将更简单,更容易推理和维护.
您还可以将这些类型放在单独的模块中,这将解决字段名称冲突.对于最终的API Block将有约非常简单的模式匹配,并转发给专门的职能Binary和Unary.
这种方法是可扩展的.无论您的实体或问题有多复杂,您都可以随意添加其他级别的分解.
这种类型的一个问题是访问器功能不再是完全的,这在当天是相当不赞成的,这是有充分理由的.这可能就是他们在书中避免的原因.
IMO,多构造函数记录原则上仍然很好,只需要理解标签不应该用作访问器函数.但它们仍然非常有用,特别是RecordWildCards扩展.
在许多图书馆中都可以找到这种类型.当构造者被隐藏时,你绝对是好的.