2010-05-08 18 views
1

En F # J'ai une fonction qui retourne les instances de System.Linq.Expression:type F # retour contrainte

and System.Object with 
    member this.ToExpression() = 
    match this with 
    | :? System.Int32 -> Expression.Constant(this) :> Expression 
    | :? System.Boolean -> Expression.Constant(this) :> Expression 
    | :? Tml.Runtime.Seq as s -> s.ToExpression() 
    | _ -> failwith "bad expression" 

Si j'omettez le type coercitions sur le retour des valeurs F # déduira le type de retour de la fonction à ConstantExpression . Ma première pensée était de marquer explicitement le type de retour comme étant: #Expression, mais cela n'a pas fonctionné. Existe-t-il une manière plus élégante de faire cela qui n'implique pas de lancer manuellement les types de retour au type le plus générique?

Merci.

Edit: Merci à vous tous pour les réponses. Je vais aller avec le type de retour explicite + scénario upcast.

Répondre

4

Voici quelques façons que vous pouvez préférer:

open System.Linq.Expressions 

type System.Object with 
    member this.ToExpression() : Expression = // explicit 
     match this with 
     | :? System.Int32 -> upcast Expression.Constant(this) // upcast 
     | :? System.Boolean -> Expression.Constant(this) :> _ // _ 
     | _ -> failwith "bad expression" 

En déclarant explicitement le type de retour sur la déclaration member, vous pouvez alors en déduire dans le corps, par exemple via _ en tant que "veuillez déduire ce type pour moi" ou en utilisant l'opérateur upcast qui déduira le type de conversion des contraintes.

1

Je ne pense pas qu'il y ait une façon beaucoup plus élégante d'écrire ceci, sans le vouloir.

Le compilateur requiert que toutes les branches de l'expression match aient le même type de retour et n'insèrent implicitement aucune coercition. Vous pouvez utiliser le mot clé upcast pour insérer une contrainte sans spécifier le type de cible. Dans ce cas, le compilateur utilisera d'autres informations (telles que les annotations de type) pour déterminer le type et vous n'aurez pas besoin de répéter le type:

and System.Object with 
    member this.ToExpression() : Expression = 
    match this with 
    | :? System.Int32 -> upcast Expression.Constant(this) 
    | :? System.Boolean -> upcast Expression.Constant(this) 
    | :? Tml.Runtime.Seq as s -> upcast s.ToExpression() 
    | _ -> failwith "bad expression" 

I ajouté annotation de type et upcast à chacun l'expression et l'annotation de type, de sorte que le compilateur F # infère que le upcast doit contraindre le résultat à Expression. Le seul endroit où le compilateur insère coercitions implicite est lorsque vous appelez une fonction, vous pouvez aussi écrire ce qui suit (mais je ne suis pas sûr que ce soit mieux):

// Thanks to implicit coercions, we don't even need #type 
let expr (a:Expression) = a 

// and then for example: 
| :? System.Int32 -> Expression.Constant(this) |> expr 

Pour une raison quelconque, upcast est un mot-clé , donc vous ne pouvez pas l'utiliser avec pipelining, donc définir une fonction comme celle-ci peut avoir des avantages.

1

à proprement parler ce ne supprime pas le coersion mais à mon avis, il est un peu mieux sur l'oeil (et vous permettra d'économiser un peu de taper trop :))

open System.Linq.Expressions 

let Constant obj = Expression.Constant(obj) :> Expression 

type System.Object with 
    member this.ToExpression() 
     match this with 
     | :? System.Int32 -> Constant(this) 
     | :? System.Boolean -> Constant(this) 
     | _ -> failwith "bad expression" 

puisque le type de Constant est a '-> Expression cela fonctionnera dans les deux cas. L'inconvénient est que vous devrez définir une fonction pour chacune des méthodes d'usine d'expression que vous allez utiliser.