F#中表达式的最佳表示

时间:2009-12-09 16:10:11

标签: f# expression-trees visitor-pattern discriminated-union

我正在开发一个库,用于从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

但这似乎有两个问题:

  1. SqlOrderByExpression不是SqlExpression。这使得很难使用访问者模式(这可能是问题所在?)。这意味着当遍历一个Select表达式时,我无法通过将每个表达式传递给Visit(expr:SqlExpression)的表达式遍历顺序列表。

  2. SqlOrderByExpression只是元组的类型别名,因此不会保留任何类型信息。这伤害了IMO的可读性。

  3. 有更好的方法对此进行建模吗?我尝试了继承路线,但我认为DU更容易使用(除非存在明显的困难)。

2 个答案:

答案 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

我认为受歧视的工会使这比需要的更难。我认为OrderBySqlSourceExpressionSqlOrderByExpression可以是不同的类型。如果您担心元组难以阅读(这就是我在您的情况下所做的事情),您可以将它们记录下来。

我认为当你开始表示像SqlSelectExpression这样的表达式树时,SqlExpression有区别的联合会很有用,其中语法更灵活。