Я определил следующий размеченный союз:
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)
, если я этого хочу.
Так есть ли другой способ сделать то, что я пытаюсь сделать?
P.S. Я также заметил, что если я помещаю атрибут StructuredFormatDisplay
в расширение вместо исходного типа, он не работает. Мне такое поведение не кажется правильным. Кажется, что либо компилятор F # должен добавить атрибут к определению типа, либо запретить использование атрибутов при увеличении типа.