I have identified the following discriminatory union:
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
Then I created a pretty printed function as follows:
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)
Now I want my Expr type Expr use this function for its ToString() method. For example:
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
But I can not do this because stringify is not defined yet. The answer is to define stringify as a member of Expr , but I don't want to pollute my initial type declaration with this specialized method, which will grow over time. Therefore, I decided to use an abstract method, which I could implement with the extension of the internal type further in the file. Here is what I did:
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
But I get the following compiler error:
error FS0912: This ad element is not allowed in Increase
The message does not even seem to be correct (I am not creating a type extension yet), but I understand why it complains. He doesnβt want me to create an abstract element on the discriminatory union type, because it cannot be inherited. Although I really don't want inheritance, I want it to behave like a partial class in C #, where I can finish defining it somewhere else (in this case the same file).
I ended up cheating using the late binding power of the StructuredFormatDisplay attribute along with 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
Although now sprintf and ToString output the same line, there is no way to get the output of Add (Con 2,Con 3) unlike (2 + 3) if I want to.
So is there any other way to do what I'm trying to do?
PS I also noticed that if I put the StructuredFormatDisplay attribute in addition to the original type, this will not work. This behavior does not seem right to me. It seems that either the F # compiler should add an attribute to the type definition, or prohibit the attributes when expanding the type.