F# 中表达式的最佳表示

发布于 2024-08-13 14:39:00 字数 1076 浏览 1 评论 0原文

我正在开发一个库,用于从 LINQ 表达式(基本上是 LINQ-to-SQL 的修改子集)生成 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 表达式时,我无法迭代 order by 表达式列表,将每个表达式传递给 Visit(expr:SqlExpression)。

  2. SqlOrderByExpression 只是元组的类型别名,因此不会保留任何类型信息。在我看来,这会损害可读性。

有更好的方法来建模吗?我尝试了继承路线,但我认为 DU 更容易使用(除非提到的困难)。

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:

  1. 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).

  2. 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).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

陈独秀 2024-08-20 14:39:00

当您使用 FP 技术 (DU) 时,您不需要记住 OOP 模式,只需使用高阶函数(折叠、地图、拉链等)。

如果您只想查看匹配代码的特定视图,可以使用以下活动模式:

let (|OrderByTerm|OrderByList|_|)= function
  |OrderBy x -> Some (OrderByTerm x)
  |Select (_,_,xs) -> Some (OrderByList xs)
  |_ -> None

As you work with FP technique (DU), as you don't need to remember OOP patterns, just use high-order functions (fold, map, zippers, etc).

In case you just want to specific view for your matching code, there is active patterns:

let (|OrderByTerm|OrderByList|_|)= function
  |OrderBy x -> Some (OrderByTerm x)
  |Select (_,_,xs) -> Some (OrderByList xs)
  |_ -> None
近箐 2024-08-20 14:39:00

我不确定为什么 SqlExpression 作为一个可区分联合,有 OrderBySelect 的情况。 OrderBy 中允许包含哪些内容?

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

我认为,当您开始表示像 (1 + SUM(ISNULL(Value1, Value2))) / COUNT(*) 这样的表达式树时,SqlExpression 可区分联合将会很有用。 ,其中语法更加灵活。

I'm not sure why SqlExpression, as a discriminated union, has cases for OrderBy and Select. What's allowed to go inside an OrderBy?

I think discriminated unions are making this more difficult than it needs to be. I think SqlSourceExpression, SqlOrderByExpression and SqlSelectExpression can be different types. You can make them records if you're worried about tuples being difficult to read (this is what I'd do in your situation).

I think an SqlExpression discriminated union will come in useful when you start to represent expression trees like (1 + SUM(ISNULL(Value1, Value2))) / COUNT(*), where the syntax is more flexible.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文