コード引用符Code quotations

この記事では、 コードの引用符について説明します。これは、プログラムによって F # コード式を生成して操作できる言語機能です。This article describes code quotations, a language feature that enables you to generate and work with F# code expressions programmatically. この機能を使用すると、F # コードを表す抽象構文ツリーを生成できます。This feature lets you generate an abstract syntax tree that represents F# code. 次に、アプリケーションのニーズに応じて、抽象構文ツリーを走査して処理できます。The abstract syntax tree can then be traversed and processed according to the needs of your application. たとえば、ツリーを使用すると、F # コードを生成したり、他の言語でコードを生成したりできます。For example, you can use the tree to generate F# code or generate code in some other language.

引用符で囲まれた式Quoted Expressions

引用符で囲まれた式は、コード内の f # 式であり、プログラムの一部としてコンパイルされるのではなく、f # の式を表すオブジェクトにコンパイルされます。A quoted expression is an F# expression in your code that is delimited in such a way that it is not compiled as part of your program, but instead is compiled into an object that represents an F# expression. 引用符で囲まれた式は、型情報を持つか、型情報のない2つの方法のいずれかでマークできます。You can mark a quoted expression in one of two ways: either with type information or without type information. 型情報を含める場合は、記号とを使用して、引用符で囲まれた <@ @> 式を区切ります。If you want to include type information, you use the symbols <@ and @> to delimit the quoted expression. 型情報が不要な場合は、との記号を使用し <@@ @@> ます。If you do not need type information, you use the symbols <@@ and @@>. 次のコードは、型指定された型指定のない引用符を示しています。The following code shows typed and untyped quotations.

open Microsoft.FSharp.Quotations
// A typed code quotation.
let expr : Expr<int> = <@ 1 + 1 @>
// An untyped code quotation.
let expr2 : Expr = <@@ 1 + 1 @@>

型情報を含めないと、大きな式ツリーを走査する方が高速になります。Traversing a large expression tree is faster if you do not include type information. 型指定された記号で囲まれた式の結果の型はです Expr<'T> 。ここで、型パラメーターには、F # コンパイラの型推論アルゴリズムによって決定される式の型があります。The resulting type of an expression quoted with the typed symbols is Expr<'T>, where the type parameter has the type of the expression as determined by the F# compiler's type inference algorithm. 型情報を含まないコード引用符を使用する場合、引用符で囲まれた式の型は非ジェネリック型の Exprになります。When you use code quotations without type information, the type of the quoted expression is the non-generic type Expr. 型指定されたクラスで Raw プロパティを呼び出して、 Expr 型指定のないオブジェクトを取得でき Expr ます。You can call the Raw property on the typed Expr class to obtain the untyped Expr object.

クラスでは、 Expr 引用符で囲まれた式を使用せずに、プログラムによって F # 式オブジェクトを生成できるようにする静的メソッドがいくつかあります。There are a variety of static methods that allow you to generate F# expression objects programmatically in the Expr class without using quoted expressions.

コード引用符には完全な式を含める必要があることに注意してください。Note that a code quotation must include a complete expression. たとえば、バインドの場合、バインドされた let 名前の定義とバインドを使用する追加の式の両方が必要です。For a let binding, for example, you need both the definition of the bound name and an additional expression that uses the binding. Verbose 構文では、これはキーワードに続く式です inIn verbose syntax, this is an expression that follows the in keyword. モジュールの最上位レベルでは、これはモジュール内の次の式にすぎませんが、引用符で囲まれている必要があります。At the top-level in a module, this is just the next expression in the module, but in a quotation, it is explicitly required.

したがって、次の式は無効です。Therefore, the following expression is not valid.

// Not valid:
// <@ let f x = x + 1 @>

ただし、次の式は有効です。But the following expressions are valid.

// Valid:
<@ let f x = x + 10 in f 20 @>
// Valid:
<@
    let f x = x + 10
    f 20
@>

F # の引用符を評価するには、 f # の引用符を使用する必要があります。To evaluate F# quotations, you must use the F# Quotation Evaluator. F # expression オブジェクトの評価と実行のサポートを提供します。It provides support for evaluating and executing F# expression objects.

Expr 型Expr Type

型のインスタンスは Expr F # の式を表します。An instance of the Expr type represents an F# expression. ジェネリック型と非ジェネリック型の両方 Expr が、F # ライブラリのドキュメントに記載されています。Both the generic and the non-generic Expr types are documented in the F# library documentation. 詳細については、「 Fsharp.core 名前空間引用クラス」を参照してください。For more information, see FSharp.Quotations Namespace and Quotations.Expr Class.

スプライス演算子Splicing Operators

スプライスを使用すると、リテラルコードの引用符を、プログラムまたは別のコード引用符から作成した式と組み合わせることができます。Splicing enables you to combine literal code quotations with expressions that you have created programmatically or from another code quotation. %演算子と演算子を使用すると、 %% F # 式オブジェクトをコード引用符に追加できます。The % and %% operators enable you to add an F# expression object into a code quotation. 型指定さ % れた式オブジェクトを型指定された引用符に挿入するには、演算子を使用します。型指定されていない式オブジェクトを型指定された引用符に挿入するには、演算子を使用し %% ます。You use the % operator to insert a typed expression object into a typed quotation; you use the %% operator to insert an untyped expression object into an untyped quotation. どちらの演算子も、単項前置演算子です。Both operators are unary prefix operators. このため expr 、が型の型指定されていない式である場合 Expr 、次のコードは有効です。Thus if expr is an untyped expression of type Expr, the following code is valid.

<@@ 1 + %%expr @@>

また、 expr が型の型指定された引用符である場合 Expr<int> 、次のコードが有効です。And if expr is a typed quotation of type Expr<int>, the following code is valid.

<@ 1 + %expr @>

Example

説明Description

次の例では、コード引用符を使用して F # コードを式オブジェクトに配置し、式を表す F # コードを出力する方法を示します。The following example illustrates the use of code quotations to put F# code into an expression object and then print the F# code that represents the expression. println print F # expression オブジェクト (型 Expr ) をわかりやすい形式で表示する再帰関数を含む関数が定義されています。A function println is defined that contains a recursive function print that displays an F# expression object (of type Expr) in a friendly format. Fsharp.coreおよびfsharp.coreモジュールには、式オブジェクトの分析に使用できるアクティブなパターンがいくつかあります。There are several active patterns in the FSharp.Quotations.Patterns and FSharp.Quotations.DerivedPatterns modules that can be used to analyze expression objects. この例には、F # 式に出現する可能性のあるすべてのパターンが含まれているわけではありません。This example does not include all the possible patterns that might appear in an F# expression. 認識されていないパターンは、ワイルドカードパターン () との一致をトリガーし、メソッドを使用して _ 表示します。このメソッドを使用すると、 ToString Expr 一致式に追加するアクティブパターンを知ることができます。Any unrecognized pattern triggers a match to the wildcard pattern (_) and is rendered by using the ToString method, which, on the Expr type, lets you know the active pattern to add to your match expression.

コード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 @@>

出力Output

fun (x:System.Int32) -> x + 1
a + 1
let f = fun (x:System.Int32) -> x + 10 in f 10

Example

説明Description

また、 Exprshape モジュール 内の3つのアクティブパターンを使用して、アクティブなパターンが少数の式ツリーを走査することもできます。You can also use the three active patterns in the ExprShape module to traverse expression trees with fewer active patterns. これらのアクティブなパターンは、ツリーを走査するときに、ほとんどのノードの情報をすべて必要としない場合に便利です。These active patterns can be useful when you want to traverse a tree but you do not need all the information in most of the nodes. これらのパターンを使用すると、F # の式は、次の3つのパターンのいずれかと一致します。式が ShapeVar 変数である場合、 ShapeLambda 式がラムダ式である場合、または ShapeCombination 式が他のものである場合は。When you use these patterns, any F# expression matches one of the following three patterns: ShapeVar if the expression is a variable, ShapeLambda if the expression is a lambda expression, or ShapeCombination if the expression is anything else. 前のコード例のようにアクティブなパターンを使用して式ツリーを走査する場合は、使用可能な F # 式の型をすべて処理するために、さらに多くのパターンを使用する必要があります。また、コードがより複雑になります。If you traverse an expression tree by using the active patterns as in the previous code example, you have to use many more patterns to handle all possible F# expression types, and your code will be more complex. 詳細については、「 Exprshape. の||、「スナップショット」を参照してください。また、このパターンを使用します。For more information, see ExprShape.ShapeVar|ShapeLambda|ShapeCombination Active Pattern.

次のコード例は、より複雑なトラバーサルの基礎として使用できます。The following code example can be used as a basis for more complex traversals. このコードでは、関数呼び出しを含む式に対して式ツリーが作成され add ます。In this code, an expression tree is created for an expression that involves a function call, add. 特定の 呼び出し のアクティブパターンは、 add 式ツリー内のへの呼び出しを検出するために使用されます。The SpecificCall active pattern is used to detect any call to add in the expression tree. このアクティブパターンは、呼び出しの引数を値に割り当て exprList ます。This active pattern assigns the arguments of the call to the exprList value. この場合は、2つしか存在しないため、これらの関数は引数に対して再帰的に呼び出されます。In this case, there are only two, so these are pulled out and the function is called recursively on the arguments. 結果は、 mul スプライス演算子 () を使用したへの呼び出しを表すコード引用符に挿入され %% ます。The results are inserted into a code quotation that represents a call to mul by using the splice operator (%%). println前の例の関数は、結果を表示するために使用されます。The println function from the previous example is used to display the results.

もう一方のアクティブパターン分岐のコードは同じ式ツリーを再生成するだけなので、結果として得られる式の変更はからに変更されるだけです add mulThe code in the other active pattern branches just regenerates the same expression tree, so the only change in the resulting expression is the change from add to 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

出力Output

1 + Module1.add(2,Module1.add(3,4))
1 + Module1.mul(2,Module1.mul(3,4))

関連項目See also