6

私は周りを見回して、これに対する答えを得るのに苦労しました。明らかな答えがあると確信していますが、それを見つけることができないようです。または、計算式で使用すると通過できない引用符の制限に達しました。

基本的に、計算 F# ワークフローを使用して、以下のように定義された引用符で囲まれたラムダを操作したいと考えています。これらのワークフローを一緒に構成しようとすると、問題が発生します。理想的には、let! を使用して Workflow<'Env, 'Result> インスタンスを一緒に構成したいと考えています。構文。私のやや素朴な試みは以下の通りです:

type Workflow<'Env, 'Result> = Expr<'Env -> 'Result>
type WorkflowSource<'Env, 'Result> = 'Env -> 'Result

type WorkflowBuilder() = 
    member x.Bind
        (workflow: WorkflowSource<'Env, 'OldResult>,
         selector: 'OldResult -> WorkflowSource<'Env, 'NewResult>) : WorkflowSource<'Env, 'NewResult> =
         (fun env -> (selector (workflow env) env))
    member x.Bind
        (workflow: Workflow<'Env, 'OldResult>,
         selector: 'OldResult -> WorkflowSource<'Env, 'NewResult>) 
         : Workflow<'Env, 'NewResult> =
         <@ (fun env -> (selector ((%workflow) env) env)) @>
    // This bind is where the trouble is
    member x.Bind
        (workflow: WorkflowSource<'Env, 'OldResult>,
         selector: 'OldResult -> Workflow<'Env, 'NewResult>) 
         : Workflow<'Env, 'NewResult> =
         <@ fun env -> 
                let newResultWorkflow = %(selector (workflow env))
                newResultWorkflow env @>
    member __.Return(x) = fun env -> x
    member __.ReturnFrom(x : WorkflowSource<_, _>) = x
    member __.Quote(x : Expr<WorkflowSource<_, _>>) : Workflow<_, _> = x

let workflow = new WorkflowBuilder()

3 番目の bind メンバーは、「変数 "env" は引用符でバインドされていますが、スライスされた式で使用されています」というコンパイラ エラーを表示します。問題は、どうやってそれを回避するかです。上記は、以下の単純なケースを機能させる試みとして定義しました。

let getNumber (env: EnvironmentContext) = (new Random()).Next()

let workflow1 = workflow {
    let! randomNumber = getNumber
    let customValue = randomNumber * 10
    return (globalId * customValue)
}

// From expression to non expression bind case
let workflow2a = workflow {
    let! workflow1 = workflow1
    let! randomNumber = getNumber
    return (randomNumber + workflow1)
}

// From non-expression to expression bind case
let workflow2 = workflow {
    let! randomNumber = getNumber
    let! workflow1 = workflow1
    return (randomNumber + workflow1)
}

私が達成しようとしていることが可能かどうか、それとも何か間違っているのでしょうか? 最終的な引用された式内のユーザー関数をキャプチャしながら、上記の単純なケースを機能させることは可能ですか?

編集:Tomasの回答を考慮して、WorkflowSourceタイプを使用せずに試しました。まだエラーが発生していません: System.InvalidOperationException: Microsoft.FSharp.Core.ExtraTopLevelOperators.SpliceExpression[T](FSharpExpr`1 expression) では、'%' または '%%' の最初のクラスの使用は許可されていません

type WorkflowBuilder() = 
    member x.Bind
        (workflow: Workflow<'Env, 'OldResult>,
         selector: 'OldResult -> Workflow<'Env, 'NewResult>) 
         : Workflow<'Env, 'NewResult> =
         fun env -> <@ %(selector (%(workflow env)) env) @>
    member __.Return(x) = fun Env -> <@ x @>
    member __.ReturnFrom(x: Workflow<_, _>) = x
    member __.Quote(expr: Expr<Workflow<'Env, 'Result>>) = expr
    // This run method fails
    member __.Run(x : Expr<Workflow<'Env, 'Result>>) : Workflow<'Env, 'Result> = fun (env: Expr<'Env>) -> <@ %((%x) env) @>

let workflow = new WorkflowBuilder()

// Env of type int for testing
let getRandomNumber (kernel: Expr<int>) = <@ (new Random()).Next() @> 

let workflow1 = workflow {
    let! randomNumber = getRandomNumber
    let otherValue = 2
    let! randomNumber2 = getRandomNumber
    return randomNumber + otherValue + randomNumber2
}
// This fails due to quotation slicing issue
workflow1 <@ 0 @>
4

1 に答える 1