使用计算表达式组合引用的函数

aka*_*ara 6 f# quotations computation-expression

我环顾四周,努力想要得到答案; 我确信有一个明显的答案,但我似乎无法找到它; 或者我遇到了与计算表达式一起使用时无法通过的引用限制.

基本上我想使用计算F#工作流程来处理如下定义的lambda.尝试将这些工作流程组合在一起时会出现问题.理想情况下,我想使用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()
Run Code Online (Sandbox Code Playgroud)

第三个绑定成员给出了编译器错误:"变量"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)
}
Run Code Online (Sandbox Code Playgroud)

只是想知道我想要实现的是可能还是我做错了什么?在最终引用的表达式中捕获用户函数时,是否可以使上述简单的案例工作?

编辑:我也试过没有WorkflowSource类型考虑托马斯的答案.没有运气仍有错误:System.InvalidOperationException:Microsoft.FSharp.Core.ExtraTopLevelOperators.SpliceExpression [T](FSharpExpr`1表达式)不允许使用'%'或'%%'的头类使用

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 @>
Run Code Online (Sandbox Code Playgroud)

Tom*_*cek 2

这只是一个想法的粗略草图,但我认为如果您不将工作流表示为带引号的函数,而是表示为采用带引号的环境并返回带引号的结果的函数,您可以更进一步:

type Workflow<'Env, 'Result> = Expr<'Env> -> Expr<'Result>
Run Code Online (Sandbox Code Playgroud)

那么你当然可以实现所有的绑定:

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 -> <@ %(selector (workflow %env) env) @>
Run Code Online (Sandbox Code Playgroud)

也就是说,我认为这并不是您所需要的全部 - 编译器似乎忽略了 中的代码Quote,因此即使我们添加变成 的引号WorkflowSourceWorkflow您仍然会收到错误,因为有Expr<WorkflowSource<_>>值 - 但我认为 bind 的另一个重载可能解决这个问题。

member __.Quote(x : Expr<WorkflowSource<_, _>>) : Workflow<_, _> = 
  fun env -> <@ (%x) %env @>
Run Code Online (Sandbox Code Playgroud)