我正在开发一个库,用于从LINQ表达式生成SQL(基本上是LINQ-to-SQL的修改子集)。我使用有区别的联合来建模SQL表达式,但遇到了一些(感知?)限制。我想做类似以下的事情(注意最后一行):
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
我可以做以下事情:
type SqlOrderByExpression = SqlExpression * SortDirection
...并将最后两行更改为:
| OrderBy of SqlOrderByExpression
| Select of SqlSourceExpression * SqlExpression * SqlOrderByExpression list
但这似乎有两个问题:
SqlOrderByExpression不是SqlExpression。这使得很难使用访问者模式(这可能是问题所在?)。这意味着当遍历一个Select表达式时,我无法通过将每个表达式传递给Visit(expr:SqlExpression)的表达式遍历顺序列表。
SqlOrderByExpression只是元组的类型别名,因此不会保留任何类型信息。这伤害了IMO的可读性。
有更好的方法对此进行建模吗?我尝试了继承路线,但我认为DU更容易使用(除非存在明显的困难)。
答案 0 :(得分:1)
当您使用FP技术(DU)时,因为您不需要记住OOP模式,只需使用高阶函数(折叠,地图,拉链等)。
如果您只想查看匹配代码的特定视图,则会显示活动模式:
let (|OrderByTerm|OrderByList|_|)= function
|OrderBy x -> Some (OrderByTerm x)
|Select (_,_,xs) -> Some (OrderByList xs)
|_ -> None
答案 1 :(得分:0)
我不确定为什么SqlExpression
作为受歧视的联盟,有[{1}}和OrderBy
的案例。什么允许进入Select
?
我认为受歧视的工会使这比需要的更难。我认为OrderBy
,SqlSourceExpression
和SqlOrderByExpression
可以是不同的类型。如果您担心元组难以阅读(这就是我在您的情况下所做的事情),您可以将它们记录下来。
我认为当你开始表示像SqlSelectExpression
这样的表达式树时,SqlExpression
有区别的联合会很有用,其中语法更灵活。