Quotations de code (F#)
Cette rubrique décrit les quotations de code, une fonctionnalité de langage qui vous permet de générer et d'utiliser des expressions de code F# par programmation. Cette fonctionnalité vous permet de générer une arborescence de syntaxe abstraite qui représente du code F#. L'arborescence de syntaxe abstraite peut ensuite être parcourue et traitée d'après les besoins de votre application. Par exemple, vous pouvez utiliser l'arborescence pour générer du code F# ou générer du code dans un autre langage.
Expressions quotées
Une expression quotée est une expression F# dans votre code qui est délimitée d'une telle façon qu'elle n'est pas compilée dans le cadre de votre programme, mais plutôt dans un objet qui représente une expression F#. Vous pouvez marquer une expression quotée de deux manières : soit avec des informations de type, soit sans informations de type. Si vous voulez inclure des informations de type, utilisez les symboles <@ et @> pour délimiter l'expression quotée. Si vous n'avez pas besoin des informations de type, utilisez les symboles <@@ et @@>. Le code suivant présente des quotations typées et non typées.
open Microsoft.FSharp.Quotations
// A typed code quotation.
let expr : Expr<int> = <@ 1 + 1 @>
// An untyped code quotation.
let expr2 : Expr = <@@ 1 + 1 @@>
Parcourir une grande arborescence d'expression est plus rapide si vous n'incluez pas d'informations de type. Le type résultant d'une expression quotée avec les symboles typés est Expr<'T>, où le paramètre de type a le type de l'expression, tel que déterminé par l'algorithme d'inférence de type du compilateur F#. Lorsque vous utilisez des quotations de code sans informations de type, le type de l'expression quotée est le type non générique Expr. Vous pouvez appeler la propriété Raw sur la classe Expr typée pour obtenir l'objet Expr non typé.
Diverses méthodes statiques vous permettent de générer des objets Expression F# par programmation dans la classe Expr sans utiliser d'expressions quotées.
Notez que des quotations de code doivent inclure une expression complète. Pour une liaison let, par exemple, vous avez besoin à la fois de la définition du nom lié et d'une expression supplémentaire qui utilise la liaison. Dans la syntaxe détaillée, il s'agit d'une expression qui suit le mot clé in. Au niveau supérieur d'un module, il s'agit de l'expression suivante dans le module, mais dans une quotation, elle est explicitement obligatoire.
Par conséquent, l'expression suivante n'est pas valide.
// Not valid:
// <@ let f x = x + 1 @>
En revanche, les expressions suivantes sont valides.
// Valid:
<@ let f x = x + 10 in f 20 @>
// Valid:
<@
let f x = x + 10
f 20
@>
Pour utiliser des quotations de code, vous devez ajouter une déclaration d'importation (en utilisant le mot clé open ) qui ouvre l'espace de noms Microsoft.FSharp.Quotations.
Le F# PowerPack fournit une prise en charge pour l'évaluation et l'exécution d'objets Expression F#.
Type Expr
Une instance du type Expr représente une expression F#. Les types génériques et non génériques Expr sont documentés dans la documentation de la bibliothèque F#. Pour plus d'informations, consultez Microsoft.FSharp.Quotations, espace de noms (F#) et Quotations.Expr, classe (F#).
Opérateurs d'ajout
Les opérateurs d'ajout vous permettent de combiner des quotations de code littéral avec les expressions que vous avez créées par programmation ou provenant d'une autre quotation de code. Les opérateurs %% et % vous permettent d'ajouter un objet Expression F# dans une quotation de code. Vous utilisez l'opérateur % pour insérer un objet expression typé dans une quotation typée ; vous utilisez l'opérateur %% pour insérer un objet expression non typé dans une quotation non typée. Ces deux opérateurs sont des opérateurs préfixés unaires. Donc, si expr est une expression non typée de type Expr, le code suivant est valide.
<@@ 1 + %%expr @@>
Et si expr est une quotation typée de type Expr<int>, le code suivant est valide.
<@ 1 + %expr @>
Exemple
Description
L'exemple suivant illustre l'utilisation de quotations de code pour mettre du code F# dans un objet expression, puis imprimer ce code F# qui représente l'expression. Une fonction println est définie ; elle contient une fonction récursive print qui affiche un objet Expression F# (de type Expr) dans un format convivial. Plusieurs modèles actifs figurant dans les modules Microsoft.FSharp.Quotations.Patterns et Microsoft.FSharp.Quotations.DerivedPatterns peuvent être utilisés pour analyser des objets expression. Cet exemple n'inclut pas tous les modèles possibles susceptibles d'apparaître dans une expression F#. Tout modèle non reconnu déclenche une correspondance au modèle de caractère générique (_), puis est rendu à l'aide de la méthode ToString qui, sur le type Expr, vous permet de connaître le modèle actif à ajouter à votre expression de correspondance.
Code
module Print
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.Patterns
open Microsoft.FSharp.Quotations.DerivedPatterns
let println expr =
let rec print expr =
match expr with
| Application(expr1, expr2) ->
// Function application.
print expr1
printf " "
print expr2
| SpecificCall <@@ (+) @@> (_, _, exprList) ->
// Matches a call to (+). Must appear before Call pattern.
print exprList.Head
printf " + "
print exprList.Tail.Head
| Call(exprOpt, methodInfo, exprList) ->
// Method or module function call.
match exprOpt with
| Some expr -> print expr
| None -> printf "%s" methodInfo.DeclaringType.Name
printf ".%s(" methodInfo.Name
if (exprList.IsEmpty) then printf ")" else
print exprList.Head
for expr in exprList.Tail do
printf ","
print expr
printf ")"
| Int32(n) ->
printf "%d" n
| Lambda(param, body) ->
// Lambda expression.
printf "fun (%s:%s) -> " param.Name (param.Type.ToString())
print body
| Let(var, expr1, expr2) ->
// Let binding.
if (var.IsMutable) then
printf "let mutable %s = " var.Name
else
printf "let %s = " var.Name
print expr1
printf " in "
print expr2
| PropertyGet(_, propOrValInfo, _) ->
printf "%s" propOrValInfo.Name
| String(str) ->
printf "%s" str
| Value(value, typ) ->
printf "%s" (value.ToString())
| Var(var) ->
printf "%s" var.Name
| _ -> printf "%s" (expr.ToString())
print expr
printfn ""
let a = 2
// exprLambda has type "(int -> int)".
let exprLambda = <@ fun x -> x + 1 @>
// exprCall has type unit.
let exprCall = <@ a + 1 @>
println exprLambda
println exprCall
println <@@ let f x = x + 10 in f 10 @@>
Sortie
fun (x:System.Int32) -> x + 1
a + 1
let f = fun (x:System.Int32) -> x + 10 in f 10
Exemple
Description
Vous pouvez également utiliser les trois modèles actifs du module ExprShape pour parcourir des arborescences d'expression avec moins de modèles actifs. Ces modèles actifs peuvent être utiles lorsque vous souhaitez parcourir une arborescence, mais que vous n'avez pas besoin de toutes les informations de la plupart des nœuds. Lorsque vous utilisez ces modèles, toute expression F# correspond à l'un des trois modèles suivants : ShapeVar si l'expression est une variable, ShapeLambda si l'expression est une expression lambda, ou ShapeCombination si l'expression est autre. Si vous parcourez une arborescence d'expression en utilisant les modèles actifs comme dans l'exemple de code précédent, vous devez utiliser beaucoup plus de modèles pour gérer tous les types d'expression F# possibles et votre code devient plus complexe. Pour plus d’informations, consultez ExprShape.ShapeVar|ShapeLambda|ShapeCombination, modèle actif (F#).
L'exemple de code suivant peut être utilisé comme base pour des parcours plus complexes. Dans ce code, une arborescence d'expression est créée pour une expression qui implique un appel de fonction, add. Le modèle actif SpecificCall est utilisé pour détecter tout appel à add dans l'arborescence d'expression. Ce modèle actif assigne les arguments de l'appel à la valeur exprList. Dans ce cas, il n'y en a que deux, donc ils sont extraits et la fonction est appelée de manière récursive sur les arguments. Les résultats sont insérés dans une quotation de code qui représente un appel à mul à l'aide de l'opérateur d'ajout (%%). La fonction println de l'exemple précédent est utilisée pour afficher les résultats.
Le code dans les autres branches de modèle actif régénère simplement la même arborescence d'expression, donc la seule modification dans l'expression résultante est la modification d'add en mul.
Code
module Module1
open Print
open Microsoft.FSharp.Quotations
open Microsoft.FSharp.Quotations.DerivedPatterns
open Microsoft.FSharp.Quotations.ExprShape
let add x y = x + y
let mul x y = x * y
let rec substituteExpr expression =
match expression with
| SpecificCall <@@ add @@> (_, _, exprList) ->
let lhs = substituteExpr exprList.Head
let rhs = substituteExpr exprList.Tail.Head
<@@ mul %%lhs %%rhs @@>
| ShapeVar var -> Expr.Var var
| ShapeLambda (var, expr) -> Expr.Lambda (var, substituteExpr expr)
| ShapeCombination(shapeComboObject, exprList) ->
RebuildShapeCombination(shapeComboObject, List.map substituteExpr exprList)
let expr1 = <@@ 1 + (add 2 (add 3 4)) @@>
println expr1
let expr2 = substituteExpr expr1
println expr2
Sortie
1 + Module1.add(2,Module1.add(3,4))
1 + Module1.mul(2,Module1.mul(3,4))