Составление цитируемых функций с использованием выражений вычислений

Я оглянулся и изо всех сил пытался получить ответ на это; Я уверен, что есть очевидный ответ, но я просто не могу его найти; или я столкнулся с ограничением цитат, которое не могу передать при использовании с выражениями вычислений.

По сути, я хочу работать с лямбда-кавычками, определенными, как показано ниже, используя вычислительный рабочий процесс F#. Проблема возникает при попытке объединить эти рабочие процессы. В идеале я хочу собрать экземпляры Workflow<'Env,' Result> вместе, используя 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()

Третий член связывания дает мне ошибку компилятора: "Переменная"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)
}

Просто интересно, возможно ли то, чего я пытаюсь достичь, или я делаю что-то не так? Можно ли заставить приведенные выше простые случаи работать во время захвата пользовательских функций внутри окончательного выражения в кавычках?

РЕДАКТИРОВАТЬ: Я также пытался без типа 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 @>

1 ответ

Это всего лишь грубый набросок идеи, но я думаю, что вы можете пойти дальше, если представите рабочий процесс не как функцию в кавычках, а как функцию, которая принимает среду в кавычках и возвращает результат в кавычках:

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 @>
Другие вопросы по тегам