我定义了以下有区别的联合:
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)
那么还有其他方法可以做我想做的事情吗?
PS我还注意到,如果我将StructuredFormatDisplay
属性放在扩充而不是原始类型上,它就不起作用。这种行为对我来说似乎不正确。似乎 F# 编译器应该将属性添加到类型定义中,或者不允许类型扩充上的属性。