F# 如何在递归区分联合中指定类型限制
F# how to specify type restriction in recursive discriminated unions
我正在尝试将我的语法定义为可区分的联合。它有两种可能的类型:int
和 datetime
以及 Add
和 Mul
的数学运算符。
Add
适用于 int
和 datetime
(作为在 int 中添加天数)
Mul
仅适用于 int
而不适用于 datetime
语法可以递归
我的语法看起来像
type MyExpression =
|Integer of int
|Date of datetime
|Add of MyExpression * MyExpression
|Mul of MyExpression * MyExpression
我写了一个解析器 (fparsec) 可以解析我语法中的文本,但我不确定如何处理 Mul
可以递归但只能在 Integer
上递归的条件。
是否有一个选项可以在我的 MyExpression
类型上定义这个限制,或者我必须在我的解析输入中处理这个?
如果您有基于类型的约束,采用通用方法可能会更容易:
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
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;}))
我正在尝试将我的语法定义为可区分的联合。它有两种可能的类型:int
和 datetime
以及 Add
和 Mul
的数学运算符。
Add
适用于 int
和 datetime
(作为在 int 中添加天数)
Mul
仅适用于 int
而不适用于 datetime
语法可以递归
我的语法看起来像
type MyExpression =
|Integer of int
|Date of datetime
|Add of MyExpression * MyExpression
|Mul of MyExpression * MyExpression
我写了一个解析器 (fparsec) 可以解析我语法中的文本,但我不确定如何处理 Mul
可以递归但只能在 Integer
上递归的条件。
是否有一个选项可以在我的 MyExpression
类型上定义这个限制,或者我必须在我的解析输入中处理这个?
如果您有基于类型的约束,采用通用方法可能会更容易:
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
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;}))