对于 DSL 语法树的表示,我有表示这棵树的数据类型。在这棵树中的几个地方,我得到了相当多的子元素,这些子元素是可选的和/或具有“*”多重性。所以一种数据类型可能看起来像
data SpecialDslExpression = MyExpression String [Int] Double [String] (Maybe Bool)
我正在寻找的是一种无需指定所有参数即可构造这种类型的可能性,假设我对每个参数都有一个有效的默认值。使用场景是这样的,我需要创建许多类型的实例,并给出或省略其参数的各种组合(大多数时候是两个或三个),但很少是全部。将参数分组为子类型不会让我走得更远,因为参数组合不遵循可以改善分割问题的模式。
我可以定义具有不同参数组合的函数,以使用其余的默认值创建类型,但我最终可能会得到相当多的难以正确命名的函数,因为可能无法为
createWithFirstAndThirdParameter
的想法在给定的上下文中。所以最后问题归结为:是否有可能创建这样的数据类型或对其进行抽象,从而为我提供诸如可以指定或省略的可选参数之类的东西?
最佳答案
我会建议镜头和默认实例的组合。如果您尚未导入 Control.Lens
在你一半的模块中,现在是时候开始了!镜头到底是什么东西?镜头是一个 getter 和一个 setter 组合成一个函数。而且它们非常可组合。每当您需要访问或修改数据结构的某些部分但您认为记录语法笨拙时,镜头就在那里。
因此,您需要做的第一件事 - 启用 TH 并导入 Control.Lens
.
{-# LANGUAGE TemplateHaskell #-}
import Control.Lens
您需要对数据类型进行的修改是为所有字段添加名称,如下所示:
data SpecialDslExpression = MyExpression { _exprType :: String
, _exprParams :: [Int]
, _exprCost :: Double
, _exprComment :: [String]
, _exprLog :: Maybe Bool
} deriving Show
字段名称开头的下划线对于下一步很重要。因为现在我们要为场生成镜头。我们可以要求 GHC 使用 Template Haskell 为我们做这件事。
$(makeLenses ''SpecialDslExpression)
然后需要做的最后一件事是构造一个“空”实例。请注意,没有人会静态检查您是否确实填写了所有必填字段,因此您最好添加
error
到这些字段,所以你至少会得到一个运行时错误。像这样的东西:emptyExpression = MyExpression (error "Type field is required!") [] 0.0 [] Nothing
现在你准备好了!您不能使用
emptyExpression
,它会在运行时失败:> emptyExpression
MyExpression {_exprType = "*** Exception: Type field is required!
但!只要您填充类型字段,您将是金色的:
> emptyExpression & exprType .~ "Test expression"
MyExpression { _exprType = "Test expression"
, _exprParams = []
, _exprCost = 0.0
, _exprComment = []
, _exprLog = Nothing
}
如果需要,您也可以一次填写多个字段。
> emptyExpression & exprType .~ "Test expression"
| & exprLog .~ Just False
| & exprComment .~ ["Test comment"]
MyExpression { _exprType = "Test expression"
, _exprParams = []
, _exprCost = 0.0
, _exprComment = ["Test comment"]
, _exprLog = Just False
}
您还可以使用镜头将功能应用于字段,或查看字段的字段内部,或修改任何其他现有表达式等。我绝对建议看看你能做什么!
关于Haskell:为具有许多字段的数据类型定义适当的接口(interface),我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18627107/