2016-12-28 9 views
10

Я пытаюсь определить свою грамматику как дискриминационный союз. Он имеет два возможных типа: int и datetime и математические операторы Add и Mul. Add работы по int и datetime (как это добавить дни в межд) Mul работает только на int, а не на datetime Грамматика может быть рекурсивнымF # как указать ограничение типа в рекурсивных дискриминационных объединениях

Моя грамматика выглядит

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

Я написал парсер (fparsec), который может анализировать текст в моей грамматике, но я не уверен, как обращаться с условием, что Mul может быть рекурсивным, но только на Integer.

Есть ли возможность определить это ограничение для моего типа MyExpression или мне нужно обработать это в моем разобранном вводе?

ответ

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;})) 
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 
+1

Это обрабатывает некоторые ограничения, но не все из них - ниже не является разумным выражением, но тип проверки: 'Add (Date (DateTime.Now), Mul (Integer 1, Integer 2)) ' –