Mi sono guardato intorno e ho faticato per ottenere una risposta a questo; Sono sicuro che c'è una risposta ovvia, ma non riesco a trovarlo; o ho raggiunto una limitazione di quotazioni che non posso superare se usato con espressioni di calcolo.Comporre funzioni quotate usando le espressioni di calcolo
Fondamentalmente voglio lavorare con un lambda quotato definito come sotto utilizzando un flusso di lavoro F # di calcolo. Il problema si presenta quando si tenta di comporre questi flussi di lavoro insieme. Idealmente, voglio comporre le istanze di Workflow < "Env", utilizzando insieme let! sintassi. Il mio tentativo un po 'ingenuo è qui sotto:
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()
Il terzo membro legano mi dà l'errore del compilatore: "La variabile 'env' è legato in una citazione, ma utilizzato in un'espressione fette" che fa un pò senso. La domanda è come faccio a girarla. Ho definito il precedente come un tentativo di provare a far funzionare i semplici casi sotto.
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)
}
Basta chiedersi se ciò che sto cercando di ottenere è possibile o sto facendo qualcosa di sbagliato? È possibile far funzionare i suddetti casi semplici durante l'acquisizione delle funzioni utente all'interno dell'espressione quotata finale?
EDIT: Ho anche provato senza il tipo WorkflowSource tenendo conto della risposta di Tomas. Nessuna fortuna ancora con l'errore: System.InvalidOperationException: usi di prima classe di '%' o '%%' non è permesso a Microsoft.FSharp.Core.ExtraTopLevelOperators.SpliceExpression [T] (espressione 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 @>
Si scopre che la definizione di 'Quote' è del tutto irrilevante - il compilatore si protende sguardi per la presenza di un membro denominato' Quote' ma non sempre lo invoca (solo cita il corpo del calcolo dell'espressione indipendentemente come 'Quote' è implementato). Questo è un IMO estremamente strano, ma è così che funzionano le espressioni di calcolo. – kvb
@kvb Sì, è così che mi è sembrato. Abbastanza strano ... Suppongo che potresti aggiungere dummy 'Quote' e inserire il codice in' Run'! –
Sì, credo che sia l'approccio standard. – kvb