F# как указать ограничение типа в рекурсивных дискриминационных объединениях
Я пытаюсь определить мою грамматику как дискриминационный союз. У него есть два возможных типа: 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
типа или я должен обрабатывать это в моем разборе ввода?
2 ответа
Дизайн, предложенный Асти, также будет моим первым выбором. В зависимости от ваших требований, это может быть все, что вам нужно.
Это, однако, также позволяет вам составить выражение как
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;}))
Если у вас есть ограничения на основе типов, для общего подхода может быть проще:
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