3

我正在尝试将一个小型编译器从 C# 移植到 F#,以利用模式匹配和可区分联合等功能。目前,我正在使用基于 System.Linq.Expressions 的模式对 AST 进行建模:一个抽象基“表达式”类、每个表达式类型的派生类和一个 NodeType 枚举,允许在不进行大量转换的情况下切换表达式。我曾希望使用 F# 有区别的联合来大大减少这种情况,但我遇到了几个看似限制:

  • 强制公共默认构造函数(我想对表达式构造进行类型检查和参数验证,就像 System.Linq.Expressions 对它的静态工厂方法所做的那样)
  • 缺少命名属性(似乎这在 F# 3.1 中已修复)
  • 无法直接引用案例类型。例如,似乎我不能声明一个仅从联合中获取一种类型的函数(例如let f (x : TYPE) = x编译为Expression(联合类型)而不是为Addor Expression.Add。这似乎牺牲了我的 C# 方法的一些类型安全性。

这些或设计模式是否有很好的解决方法可以让它们不那么令人沮丧?

4

2 回答 2

12

我认为,您对 DU 是类层次结构的想法有点过于执着了。真的,将其视为数据更有帮助。像这样:

  • 强制公共默认构造函数(我想对表达式构造进行类型检查和参数验证,就像 System.Linq.Expressions 对它的静态工厂方法所做的那样)

DU 只是数据,很像字符串或数字,而不是功能。你为什么不做一个返回你的函数Expression option来表达,你的数据可能是无效的。

  • 缺少命名属性(似乎这在 F# 3.1 中已修复)

如果你觉得你需要命名属性,你可能有一个不合适的类型,比如string * string * string * int * float你的Expression. 最好做一个记录,比如AddInfo让你的DU案例使用它,比如说| Add of AddInfo。这样你就拥有了模式匹配、智能感知等方面的属性。

  • 无法直接引用案例类型。例如,似乎我不能声明一个仅从联合中获取一种类型的函数(例如,让 f (x : TYPE) = x 编译为表达式(联合类型),而不是为 Add 或 Expression.Add。这似乎牺牲了我的 C# 方法的一些类型安全性。

你不能要求某些东西是Add这样的,但你肯定可以写一个函数,它需要一个AddInfo. 另外,您始终可以以单子方式执行此操作,并且具有接受任何Expression且仅返回option. 在这种情况下,您可以进行模式匹配,即您的输入是适当的类型,None如果不是则返回。在调用站点,您可以在良好情况下“使用”该值,使用Option.bind.

基本上尽量不要将 DU 视为一组类,而实际上只是数据的案例。有点像枚举。

于 2013-11-12T14:29:54.830 回答
10

您可以将实现设为私有。这允许您在实现中充分利用 DU,但向 API 的使用者提供有限的视图。请参阅有关记录的相关问题的答案(尽管它也适用于 DU)。

编辑

我在 MSDN 上找不到语法,但这里是:

type T =
  private
  | A
  | B

private这里的意思是“模块私有”。

于 2013-11-12T15:43:07.110 回答