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

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

В основном я хочу работать с цитируемой лямбдой, определенной, как показано ниже, с использованием вычислительного рабочего процесса 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 @>

person akara    schedule 15.09.2015    source источник


Ответы (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 @>
person Tomas Petricek    schedule 15.09.2015
comment
Оказывается, определение Quote совершенно неактуально - компилятор juts ищет наличие члена с именем Quote, но никогда не вызывает его (он просто цитирует тело вычислительного выражения независимо от того, как реализовано Quote). Это чрезвычайно странно, ИМО, но именно так работают выражения вычислений. - person kvb; 15.09.2015
comment
@kvb Да, мне так показалось. Довольно странно ... Я полагаю, вы могли бы добавить фиктивный Quote и поместить реальный код в Run! - person Tomas Petricek; 15.09.2015
comment
Да, я считаю, что это стандартный подход. - person kvb; 15.09.2015
comment
Спасибо всем за предложение. Отредактировал свой исходный пост, чтобы учесть попытку. По-прежнему возникают ошибки, но проверка компиляции по крайней мере трех вычислительных выражений. - person akara; 16.09.2015
comment
В качестве обновления, как бы я ни старался, продолжать выдавать эту конкретную ошибку: System.InvalidOperationException: использование «%» или «%%» в первом классе не разрешено. Я тоже не думаю, что эта идея сработает. Метод цитаты построителя вычислительных выражений дает мне Expr ‹Expr‹ 'Env ›-› Exp ‹' Result ››, который я не могу понять, как распаковать обратно в рабочий процесс, который является Expr‹ 'Env ›-› Exp ‹'Результат› без получения вышеуказанной ошибки во время выполнения. Обдумывание этой идеи во время компиляции проверок, как уже упоминалось, вероятно, не сработает, по крайней мере, с моими текущими знаниями цитат. - person akara; 17.09.2015