2016-08-19 1 views
3

Jouant avec le framework Luis + bot de Microsoft, mon idée de «ce qui ferait du bon type» a commencé à picoter. Malheureusement, les fournisseurs de type ne peuvent pas produire d'unions discriminées. J'espérais faire quelque chose comme ce qui suit, mais il est impossible:Quel est un bon moyen d'écrire un fournisseur de type F # pour Microsoft?

type Luis = LuisProvider<@"LuisId",@"LuisPasskey"> 
let IntentMatcher Intent = 
    match intent with 
    | Luis.Intents.Greeting -> GreetingHandler() 
    | Luis.Intents.SetAlarm title startDate startTime -> AlarmHandler title startDate startTime 
    | _ -> CouldNotUnderstand() 

Les intentions Luis et leurs paramètres sont disponibles par l'intermédiaire Apis ce qui les rend d'excellents candidats pour typeProviderization

Pour référence est ici gestionnaire d'un exemple C# bot (que je pense pourrait être plus propre et plus sÛR type en F #):

public const string Entity_Alarm_Title = "builtin.alarm.title"; 
public const string Entity_Alarm_Start_Time = "builtin.alarm.start_time"; 
public const string Entity_Alarm_Start_Date = "builtin.alarm.start_date"; 
public const string DefaultAlarmWhat = "default"; 

[LuisIntent("builtin.intent.alarm.set_alarm")] 
public async Task SetAlarm(IDialogContext context, LuisResult result) 
{ 
     EntityRecommendation title; 
     if (!result.TryFindEntity(Entity_Alarm_Title, out title)) 
     { 
      title = new EntityRecommendation(type: Entity_Alarm_Title) { Entity = DefaultAlarmWhat }; 
     } 
     EntityRecommendation date; 
     if (!result.TryFindEntity(Entity_Alarm_Start_Date, out date)) 
     { 
      date = new EntityRecommendation(type: Entity_Alarm_Start_Date) { Entity = string.Empty }; 
     } 
     EntityRecommendation time; 
     if (!result.TryFindEntity(Entity_Alarm_Start_Time, out time)) 
     { 
      time = new EntityRecommendation(type: Entity_Alarm_Start_Time) { Entity = string.Empty }; 
     } 
     var parser = new Chronic.Parser(); 
     var span = parser.Parse(date.Entity + " " + time.Entity); 
     if (span != null) 
     { 
      var when = span.Start ?? span.End; 
      var alarm = new Alarm() { What = title.Entity, When = when.Value }; 
      this.alarmByWhat[alarm.What] = alarm; 
      string reply = $"alarm {alarm} created"; 
      await context.PostAsync(reply); 
     } 
     else 
     { 
      await context.PostAsync("could not find time for alarm"); 
     } 
     context.Wait(MessageReceived); 
} 

Quoi qu'il en soit, la question est: ce que quelqu'un avec des fournisseurs de type de construction plus d'expérience ont de bonnes idées sur la façon dont je peux structurer un dsl lisible qui est ac t-il possible de construire?

Répondre

7

Je ne suis pas particulièrement familier avec le framework bot, mais je peux commenter les unions discriminées - nous sommes confrontés au même problème dans les données F #. Si vous avez <One name="string" /><Two id="42" />, il serait bon de fournir une union discriminée avec les cas One of string et Two of int. Ce que nous faisons est la place que nous fournissons un type:

type OneOrTwo = 
    member One : option<string> 
    member Two : option<int> 

Vous pouvez suivre le même schéma et d'exposer API qui ressemble à ceci:

type Luis = LuisProvider<"LuisId", "LuisPasskey"> 

let intentMatcher (intent:Luis.Intents) = 
    match intent.Greetings, intent.SetAlarm with 
    | Some(), _ -> greetingHandler() 
    | _, Some(title, startDate, startTime) -> alarmHandler title startDate startTime 
    | _ -> couldNotUnderstand() 

Luis.Connect().OnIntent 
|> Observable.subscribe intentMatcher 

Il est pas tout à fait aussi élégant que les syndicats discriminés, mais cela devrait être techniquement faisable.

Je suppose qu'une autre alternative serait d'exposer des gestionnaires pour les actions individuelles comme des événements séparés et alors vous pourriez écrire quelque chose comme ceci:

type Luis = LuisProvider<"LuisId", "LuisPasskey"> 

let luis = Luis.Connect() 

luis.BuiltIn.Greetings 
|> Observable.add greetingHandler 

luis.BuiltIn.SetAlarm 
|> Observable.add (fun (title, startDate, startTime) -> 
    alarmHandler title startDate startTime) 

Maintenant que je pense, ce serait probablement plus agréable, mais cela dépend du type d'utilisation typique du framework bot.

+0

Tomas vous êtes une star! Le dernier des deux semble parfait :) –