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
Другие вопросы по тегам