受歧视的工会的F#限制

Cha*_*ion 3 f# discriminated-union

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

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

对于这些或设计模式是否有良好的解决方法,使它们不那么令人沮丧?

Dan*_*ian 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视为一组类,但实际上只是数据的情况.有点像枚举.

  • 那么,您想要确保哪种保证?可能至少其中一些你可以作为DU本身编码到类型系统中.这样你甚至可能不需要检查它,而是让它们由编译器强制执行.当谈到记录时,你也可以只匹配一些属性,而不关心其他属性.这使得模式匹配非常简洁.有一个只接受案件具体数据的功能有什么不对? (4认同)

Dan*_*iel 10

您可以将实现设为私有.这使您可以在实现中充分发挥DU的功能,但对API的消费者提供有限的视图.有关记录的相关问题,请参阅此答案(尽管它也适用于DUs).

编辑

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

type T =
  private
  | A
  | B
Run Code Online (Sandbox Code Playgroud)

private 这里的意思是"模块私有".