我已经环顾四周,努力得到这个问题的答案;我肯定有一个明显的答案,但我似乎找不到;或者我遇到了引号的限制,当与计算表达式一起使用时,我无法通过。
基本上,我想使用引用的lambda定义如下,使用计算f#工作流。当试图将这些工作流组合在一起时,问题就出现了。理想情况下,我想组合工作流<'Env, '结果>实例一起使用let!语法。我有点天真的尝试如下:
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()
第三个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类型。仍然没有运气的错误:系统。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 @>
这只是一个想法的粗略草图,但我认为如果您不将工作流表示为引用函数,而是作为一个采用引用环境并返回引用结果的函数,则可以进一步:
type Workflow<'Env, 'Result> = Expr<'Env> -> Expr<'Result>
那么你当然可以实现所有的绑定:
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) @>
也就是说,我认为这并不是你需要的全部-似乎编译器忽略了Quote
中的代码,所以即使我们添加引号将WorkflowSource
变成Workflow
,你仍然会得到错误,因为有Expr<WorkflowSource<_>>
值-但我认为另一个绑定过载可能会解决这个问题。
member __.Quote(x : Expr<WorkflowSource<_, _>>) : Workflow<_, _> =
fun env -> <@ (%x) %env @>