F#如何在递归区分联合中指定类型限制

时间:2016-12-28 00:58:04

标签: f# restriction f#-3.0

我试图将我的语法定义为一个有区别的联盟。它有两种可能的类型:intdatetime以及AddMul的数学运算符。 Add适用于intdatetime(在int中添加天数) Mul仅适用于int,而不适用于datetime 语法可以递归

我的语法看起来像

type MyExpression =
|Integer of int
|Date of datetime
|Add of MyExpression * MyExpression
|Mul of MyExpression * MyExpression

我编写了一个解析器(fparsec),它可以解析我的语法中的文本,但我不知道如何处理Mul可以递归但只能在Integer上的条件。

是否可以选择在我的MyExpression类型上定义此限制,还是必须在我的解析输入中处理此限制?

2 个答案:

答案 0 :(得分:9)

Asti建议的设计也是我的首选。根据您的要求,这可能就是您所需要的一切。

但它也可以编译像

这样的表达式
Add(Val(System.Console.Out), Val(System.Console.Error))

这可能不是你想要的。

或者,你可以模拟这样的表达式:

open System

type IntExpression =
| Integer of int
| Mul of IntExpression * IntExpression
| Add of IntExpression * IntExpression

type DateTimeExpression =
| Date of DateTime
| Add of DateTimeExpression * DateTimeExpression

type MyExpression =
| IntExpression of IntExpression
| DateTimeExpression of DateTimeExpression

这显然是一个更详细的类型定义,但它确实体现了一个表达式可以包含整数或DateTime值的叶节点,而没有其他值的规则 - 如果这是规则你想强制执行。

我并没有声称这更好;我只提供替代品。

用法:

> IntExpression(Mul(IntExpression.Add(Integer(1), Integer(2)),Integer 3));;
val it : MyExpression =
  IntExpression (Mul (Add (Integer 1,Integer 2),Integer 3))
> DateTimeExpression(Add(Date(DateTime.MinValue),Date(DateTime.MinValue)));;
val it : MyExpression =
  DateTimeExpression
    (Add
       (Date 01.01.0001 00:00:00 {Date = 01.01.0001 00:00:00;
                                  Day = 1;
                                  DayOfWeek = Monday;
                                  DayOfYear = 1;
                                  Hour = 0;
                                  Kind = Unspecified;
                                  Millisecond = 0;
                                  Minute = 0;
                                  Month = 1;
                                  Second = 0;
                                  Ticks = 0L;
                                  TimeOfDay = 00:00:00;
                                  Year = 1;},
        Date 01.01.0001 00:00:00 {Date = 01.01.0001 00:00:00;
                                  Day = 1;
                                  DayOfWeek = Monday;
                                  DayOfYear = 1;
                                  Hour = 0;
                                  Kind = Unspecified;
                                  Millisecond = 0;
                                  Minute = 0;
                                  Month = 1;
                                  Second = 0;
                                  Ticks = 0L;
                                  TimeOfDay = 00:00:00;
                                  Year = 1;}))

答案 1 :(得分:7)

如果您有基于类型的约束,则可能更容易采用通用方法:

type MyExpression<'t> =
|Val of 't
|Mul of MyExpression<int> * MyExpression<int>
|Add of MyExpression<'t> * MyExpression<'t>

let Integer (x:int) = Val(x)
let Date (x:DateTime) = Val(x)

用法:

Mul(Integer(1), Integer(2)) //compiles
Mul(Date(DateTime.Now), Date(DateTime.Now)) //error