Lets say I'm building a parser for a domain-specific language in F#.
I've defined a discriminated union to represent expressions:
type Expression =
| Equality of Expression*Expression
| NonEquality of Expression*Expression
| Or of Expression*Expression
| And of Expression*Expression
| If of Expression*Expression
| IfElse of Expression*Expression*Expression
| Bool of bool
| Variable of string
| StringLiteral of string
Now, I've built up an AST of type Expression
and want to generate code for it.
I have one function which does type inference and type checking on an expression.
It's defined like
let rec InferType expr =
match expr with
| Equality(e1,e2) -> CheckTypes (InferType e1) (InferType e2)
| Or(e1,e2) -> CheckTypes (InferType e1) (InferType e2)
| And(e1,e2) -> CheckTypes (InferType e1) (InferType e2)
...
And I have another function to generate code which follows a similar pattern: Take an expression, write pattern-matching statements for each item in the union.
My question is: Is this the idiomatic way to do it in F#?
It seems to me that it would be cleaner if each member of the union defined its own InferType
and GenerateCode
locally with it.
If I were using C#, I would define some abstract base class called Expression
with virtual methods for InferType
and GenerateCode
and then override them in each subclass.
Is there any other way to do this?