私は次の差別的共用体を定義しました。
type Expr =
| Con of Num
| Var of Name
| Add of Expr * Expr
| Sub of Expr * Expr
| Mult of Expr * Expr
| Div of Expr * Expr
| Pow of Expr * Expr
次に、次のようにきれいな印刷関数を作成しました。
let rec stringify expr =
match expr with
| Con(x) -> string x
| Var(x) -> string x
| Add(x, y) -> sprintf "(%s + %s)" (stringify x) (stringify y)
| Sub(x, y) -> sprintf "(%s - %s)" (stringify x) (stringify y)
| Mult(x, y) -> sprintf "(%s * %s)" (stringify x) (stringify y)
| Div(x, y) -> sprintf "(%s / %s)" (stringify x) (stringify y)
| Pow(x, y) -> sprintf "(%s ** %s)" (stringify x) (stringify y)
今、私は自分の型をそのメソッドExpr
にこの関数を使用させたいと思っています。ToString()
例えば:
type Expr =
| Con of Num
| Var of Name
| Add of Expr * Expr
| Sub of Expr * Expr
| Mult of Expr * Expr
| Div of Expr * Expr
| Pow of Expr * Expr
override this.ToString() = stringify this
しかし、stringify
まだ定義されていないため、これを行うことはできません。答えはStringify
のメンバーとして定義するExpr
ことですが、時間の経過とともに成長し続けるこの特殊なメソッドで、最初の型宣言を汚染したくありません。したがって、ファイルのさらに下にある組み込み型拡張機能を使用して実装できる抽象メソッドを使用することにしました。これが私がしたことです:
type Expr =
| Con of Num
| Var of Name
| Add of Expr * Expr
| Sub of Expr * Expr
| Mult of Expr * Expr
| Div of Expr * Expr
| Pow of Expr * Expr
override this.ToString() = this.Stringify()
abstract member Stringify : unit -> string
しかし、次のコンパイラ エラーが発生します。
エラー FS0912: この宣言要素は拡張では許可されていません
メッセージは正しくないように見えますが (型拡張はまだ作成していません)、不平を言っている理由は理解できます。継承できないため、識別された共用体型で抽象メンバーを作成したくありません。継承は本当にしたくありませんが、C# の部分クラスのように振る舞い、別の場所 (この場合は同じファイル) で定義を完了できるようにしたいと考えています。
StructuredFormatDisplay
私は、属性の遅延結合力を次のものとともに使用して、「不正行為」をしましたsprintf
。
[<StructuredFormatDisplay("{DisplayValue}")>]
type Expr =
| Con of Num
| Var of Name
| Add of Expr * Expr
| Sub of Expr * Expr
| Mult of Expr * Expr
| Div of Expr * Expr
| Pow of Expr * Expr
override this.ToString() = sprintf "%A" this
/* stringify function goes here */
type Expr with
member public this.DisplayValue = stringify this
現在sprintf
とToString
両方が同じ文字列を出力しますが、出力が必要な場合とAdd (Con 2,Con 3)
は対照的に、出力を取得する方法はありません(2 + 3)
。
それで、私がやろうとしていることを行う他の方法はありますか?
StructuredFormatDisplay
PS また、元のタイプではなくオーグメンテーションに属性を配置すると、機能しないことにも気付きました。この振る舞いは私には正しくないようです。F# コンパイラは、属性を型定義に追加するか、型拡張の属性を許可しないようにする必要があるようです。