I am trying to port a small compiler from C# to F# to take advantage of features like pattern matching and discriminated unions. Currently, I am modeling the AST using a pattern based on System.Linq.Expressions: A an abstract base "Expression" class, derived classes for each expression type, and a NodeType enum allowing for switching on expressions without lots of casting. I had hoped to greatly reduce this using an F# discriminated union, but I've run into several seeming limitations:
- Forced public default constructor (I'd like to do type-checking and argument validation on expression construction, as System.Linq.Expressions does with it's static factory methods)
- Lack of named properties (seems like this is fixed in F# 3.1)
- Inability to refer to a case type directly. For example, it seems like I can't declare a function that takes in only one type from the union (e. g.
let f (x : TYPE) = x
compiles forExpression
(the union type) but not forAdd
orExpression.Add
. This seems to sacrifice some type-safety over my C# approach.
Are there good workarounds for these or design patterns which make them less frustrating?