2015-09-15 19 views
6

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 @> 

risposta

2

questo è solo un abbozzo di un'idea, ma penso che si può ottenere di più se rappresentate flusso di lavoro non in funzione citato, ma come una funzione che prende un ambiente citato e restituisce un risultato citato:

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

Quindi è possibile implementare tutti i vincoli:

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) @> 

Detto questo, credo che questo non è abbastanza tutto ciò che serve - sembra che il compilatore sta ignorando il codice in Quote, quindi, anche se aggiungiamo citazione che trasforma WorkflowSource in Workflow, è ancora ottenere gli errori, perché ci sono Expr<WorkflowSource<_>> valori - ma penso che un altro sovraccarico di legatura potrebbe risolverlo.

member __.Quote(x : Expr<WorkflowSource<_, _>>) : Workflow<_, _> = 
    fun env -> <@ (%x) %env @> 
+1

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

+0

@kvb Sì, è così che mi è sembrato. Abbastanza strano ... Suppongo che potresti aggiungere dummy 'Quote' e inserire il codice in' Run'! –

+0

Sì, credo che sia l'approccio standard. – kvb

Problemi correlati