Haskell:为具有许多字段的数据类型定义适当的接口

时间:2013-09-05 03:10:59

标签: haskell

对于DSL语法树的表示,我有表示此树的数据类型。在这个树的几个地方,我得到了许多可选的子元素和/或具有“*”多样性。因此,一种数据类型可能类似于

data SpecialDslExpression = MyExpression String [Int] Double [String] (Maybe Bool)

我正在寻找的是构建这样一种类型而不必指定所有参数的可能性,假设我有一个有效的默认值。使用场景是这样的,我需要创建许多类型的实例,其中给出或省略了其参数的各种组合(大多数情况下是两次或三次),但很少都是这些。将参数分组为子类型不会让我走得太远,因为参数组合不遵循会使分段改善问题的模式。

我可以使用不同的参数组合来定义函数,以使用其余的默认值来创建类型,但是我可能最终会有很多这样的函数很难正确命名,因为可能没有可能给出正确的在给定的上下文中命名为createWithFirstAndThirdParameter的想法。

所以最后这个问题归结为:是否有可能在它上面创建这样的数据类型或抽象,这会给我一些类似于我可以指定或省略的可选参数?

2 个答案:

答案 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