2015-09-15 9 views
6

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

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

член третьего связывания дает мне ошибку компилятора: «Переменная„окр“привязывается в цитате, но используется в нарезанного выражении», который своего рода имеет смысл. Вопрос в том, как мне обойти это. Я определил это как попытку попытаться заставить простые нижеследующие случаи работать.

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

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

EDIT: Я также пробовал без типа 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 @> 

ответ

2

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

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

Оказывается, что определение 'Quote' совершенно не имеет значения - компилятор торчит смотрит на наличие члена с именем' Quote', но не всегда ссылаться на него (он просто цитирует тело вычисления выражения, независимо от как «Цитата» реализована). Это чрезвычайно странная ИМО, но так работают выражения вычислений. – kvb

+0

@ kvb Да, так оно и было для меня. Довольно странно ... Полагаю, вы могли бы добавить манекен 'Quote' и поставить фактический код в' Run', хотя! –

+0

Да, я считаю, что это стандартный подход. – kvb