8

DSL 構文ツリーを表現するために、このツリーを表すデータ型があります。このツリー内のいくつかの場所で、オプションのサブ要素や "*" 多重度を持つサブ要素を多数取得しています。したがって、1つのデータ型は次のようになります

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

私が探しているのは、すべてのパラメーターを指定する必要なく、そのような型を構築する可能性です。それぞれに有効なデフォルトがあると仮定します。使用シナリオでは、指定または省略されたパラメーターのすべての種類の組み合わせ (ほとんどの場合 2 つまたは 3 つ) を使用して型のインスタンスを多数作成する必要がありますが、それらすべてを作成することはほとんどありません。パラメータをサブタイプにグループ化しても、パラメータの組み合わせがセグメンテーションで問題を改善するパターンに従っていないため、うまくいきません。

さまざまなパラメーターの組み合わせで関数を定義して、残りのデフォルトを使用して型を作成することもできますが、適切な名前を付けることができない可能性があるため、適切な名前を付けるのが難しくなるかなりの数の関数になる可能性があります。createWithFirstAndThirdParameter与えられた文脈でのアイデア。

したがって、結局のところ、質問は次のようになります。必要に応じて指定または省略できるオプションのパラメーターのようなものを提供するようなデータ型またはその抽象化を作成することは可能ですか?

4

2 に答える 2

12

レンズとデフォルトのインスタンスの組み合わせをお勧めします。モジュールの半分をまだインポートしていない場合はControl.Lens、今が開始するときです。とにかく、レンズとは一体何ですか?レンズは、ゲッターとセッターを 1 つの関数にマッシュしたものです。そして、それらは非常に構成可能です。データ構造の一部にアクセスしたり変更したりする必要があるが、レコード構文が扱いにくいと思うときはいつでも、レンズが役に立ちます。

したがって、最初に行う必要があるのは、TH と import を有効にすることですControl.Lens

{-# LANGUAGE TemplateHaskell #-}

import Control.Lens

データ型に必要な変更は、次のようにすべてのフィールドに名前を追加することです。

data SpecialDslExpression = MyExpression { _exprType :: String
                                         , _exprParams :: [Int]
                                         , _exprCost :: Double
                                         , _exprComment :: [String]
                                         , _exprLog :: Maybe Bool
                                         } deriving Show

次の手順では、フィールド名の先頭にあるアンダースコアが重要です。フィールドのレンズを生成したいからです。Template Haskell を使って、GHC にそれを依頼することができます。

$(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
             }

レンズを使用して、関数をフィールドに適用したり、フィールドのフィールド内を調べたり、他の既存の式を変更したりすることもできます。何ができるか見てみることをお勧めします!

于 2013-09-05T04:53:59.867 に答える
11

さて、私は実際に私のコメントを拡張します。まず、データ型をレコードとして定義します (そして、いくつかの型の同義語を投入します)。

data Example = E {
    one   :: Int,
    two   :: String,
    three :: Bool,
    four  :: Double
}

次に、デフォルトのインスタンスを作成します

defaultExample = Example 1 "foo" False 1.4

そして、ユーザーがデフォルトのフィールドを微調整して独自のデータを作成したい場合は、次のようにすることができます。

 myData = defaultExample{four=2.8}

最後に、1 つのアイテムだけをパターン マッチさせたい場合は、次を使用できます。

  foo MyData{four=a} = a
于 2013-09-05T04:31:36.553 に答える