2015-09-15 17 views
6

Rozejrzałem się i starałem się uzyskać odpowiedź na to; Jestem pewien, że istnieje oczywista odpowiedź, ale po prostu nie mogę tego znaleźć; lub trafiłem na ograniczenie cytatów, których nie mogę przekazać, gdy są używane z wyrażeniami obliczeniowymi.Tworzenie cytowanych funkcji za pomocą wyrażeń obliczeniowych

Zasadniczo chcę pracować z cytowaną wartością lambda zdefiniowaną poniżej, wykorzystując przepływ pracy F #. Problem pojawia się, próbując skomponować te przepływy pracy. Idealnie chciałbym skomponować wystąpienia Workflow < "Env," Result> razem za pomocą let! składnia. My nieco naiwna próba jest poniżej:

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

Trzeci członek wiążą daje mi błąd kompilatora: „Zmienna«env»jest związany w cytacie, ale używane w plastrach wypowiedzi”, które trochę sens. Pytanie brzmi, jak sobie z tym poradzić. Zdefiniowałem powyższe jako próbę, aby spróbować wykonać poniższe proste czynności.

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

Zastanawiam się, czy to, co próbuję osiągnąć, jest możliwe, czy też robię coś nie tak? Czy jest możliwe, aby powyższe proste przypadki działały podczas przechwytywania funkcji użytkownika w końcowym cytowanym wyrażeniu?

EDYCJA: Próbowałem również bez typu WorkflowSource, biorąc pod uwagę odpowiedź Tomas. Brak powodzenia nadal z powodu błędu: System.InvalidOperationException: Pierwsza klasa zastosowania '%' lub '%%' nie są dozwolone na Microsoft.FSharp.Core.ExtraTopLevelOperators.SpliceExpression [T] (wyrażenie 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 @> 

Odpowiedz

2

to tylko szorstki szkic pomysłu, ale myślę, że można dostać się dalej, jeśli reprezentują przepływ pracy nie jako funkcja cytowany, ale jako funkcja, która bierze kwotowań środowisko i zwraca cytowany wynik:

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

Następnie można z pewnością wdrożyć wszystkie wiązania:

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

Mimo to, myślę, że nie jest to zupełnie wystarczy - wydaje się, że kompilator ignoruje kod w Quote, więc nawet jeśli dodamy cytat, który zamienia WorkflowSource do Workflow, nadal pojawiają się błędy, ponieważ istnieje Expr<WorkflowSource<_>> wartości - ale myślę, że może to rozwiązać inne przeciążenie wiązania.

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

Okazuje się, że definicja 'Quote' jest zupełnie nieistotny - kompilator wcina spojrzenia na obecność członka nazwie' Quote' ale nigdy go nie powoływać (to po prostu cytuje ciało wyrażenie obliczeń, niezależnie od jak implementuje się 'Quote'). To jest bardzo dziwna IMO, ale tak działają wyliczenia obliczeniowe. – kvb

+0

@kvb Tak, tak to wyglądało dla mnie. Dość dziwne ... Przypuszczam, że można dodać manekina 'Quote' i umieścić rzeczywisty kod w' Run' chociaż! –

+0

Tak, uważam, że to standardowe podejście. – kvb