I'm working on a library to generate SQL from LINQ expressions (basically a modified subset of LINQ-to-SQL). I'm using discriminated unions to model the SQL expressions, but have encountered some (perceived?) limitations. I want to do something like the following (note the last line):
type SqlSourceExpression =
| Table of string
| Join of JoinType * SqlSourceExpression * SqlSourceExpression * SqlExpression //ie, left, right, predicate
and SqlExpression =
| Source of SqlSourceExpression
| OrderBy of SqlExpression * SortDirection
| Select of SqlSourceExpression * SqlExpression * OrderBy list //can't do this
I could do the following:
type SqlOrderByExpression = SqlExpression * SortDirection
...and change the last two lines to:
| OrderBy of SqlOrderByExpression
| Select of SqlSourceExpression * SqlExpression * SqlOrderByExpression list
But that appears to have two problems:
SqlOrderByExpression is not a SqlExpression. This makes it hard to use the visitor pattern (maybe herein lies the problem?). Which means when traversing a Select expression I can't iterate over the list of order by expressions passing each one to Visit(expr:SqlExpression).
SqlOrderByExpression is merely a type alias for a tuple, so no type information is preserved. That hurts readability IMO.
Is there a better way to model this? I tried the inheritance route, but I think DUs are MUCH easier to work with (barring the noted difficulty).