对于DSL语法树的表示,我有表示此树的数据类型。在这个树的几个地方,我得到了许多可选的子元素和/或具有“*”多样性。因此,一种数据类型可能类似于
data SpecialDslExpression = MyExpression String [Int] Double [String] (Maybe Bool)
我正在寻找的是构建这样一种类型而不必指定所有参数的可能性,假设我有一个有效的默认值。使用场景是这样的,我需要创建许多类型的实例,其中给出或省略了其参数的各种组合(大多数情况下是两次或三次),但很少都是这些。将参数分组为子类型不会让我走得太远,因为参数组合不遵循会使分段改善问题的模式。
我可以使用不同的参数组合来定义函数,以使用其余的默认值来创建类型,但是我可能最终会有很多这样的函数很难正确命名,因为可能没有可能给出正确的在给定的上下文中命名为createWithFirstAndThirdParameter
的想法。
所以最后这个问题归结为:是否有可能在它上面创建这样的数据类型或抽象,这会给我一些类似于我可以指定或省略的可选参数?
答案 0 :(得分:12)
我建议使用镜头组合和默认实例。如果您尚未在一半模块中导入Control.Lens
,现在是时候开始了!无论如何,镜头到底是什么?镜头是一个吸气剂,一个装订器捣碎成一个功能。它们非常易于组合。任何时候你需要访问或修改数据结构的某些部分,但你认为记录语法不实用,镜头就在那里。
因此,您需要做的第一件事 - 启用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
}
您还可以使用镜头将功能应用于字段,或查看字段的字段,或修改任何其他现有表达式等。我绝对建议你看看你能做些什么!
答案 1 :(得分:11)
好吧,我实际上会扩展我的评论。首先,将您的数据类型定义为记录(并输入几个类型的同义词)。
data Example = E {
one :: Int,
two :: String,
three :: Bool,
four :: Double
}
接下来,您将创建一个默认实例
defaultExample = Example 1 "foo" False 1.4
然后当用户想要调整默认字段以创建自己的数据时,他们可以这样做:
myData = defaultExample{four=2.8}
最后,当他们想要模式匹配一个项目时,他们可以使用
foo MyData{four=a} = a