2016-11-21 8 views
3

は、私は、ログや睡眠と戻り値のような副作用をもたらすことができ、計算を可能にする計算ワークフローを記述しようとしている遅延計算ワークフローを作成するには?

使用例は、この

let add x y = 
    compute { 
     do! log (sprintf "add: x = %d, y= %d" x y) 
     do! sleep 1000 
     let r = x + y 
     do! log (sprintf "add: result= %d" r) 
     return r 
    } 
... 
let result = run (add 100 1000) 

と私のようなものになるだろうexecuteComputationが呼び出されたときに副作用が生成されるようにします。

私の試みは

type Effect =  
    | Log of string 
    | Sleep of int 

type Computation<'t> = Computation of Lazy<'t * Effect list> 

let private bind (u : 'u, effs : Effect list) 
     (f : 'u -> 'v * Effect list) 
     : ('v * Effect list) = 
    let v, newEffs = f u 
    let allEffects = List.append effs newEffs 
    v, allEffects 

type ComputeBuilder() = 
    member this.Zero() = lazy ((), []) 

    member this.Return(x) = x, [] 

    member this.ReturnFrom(Computation f) = f.Force() 

    member this.Bind(x, f) = bind x f 

    member this.Delay(funcToDelay) = funcToDelay 

    member this.Run(funcToRun) = Computation (lazy funcToRun()) 

let compute = new ComputeBuilder() 

let log msg =(), [Log msg] 
let sleep ms =(), [Sleep ms] 

let run (Computation x) = x.Force() 

です...しかし、コンパイラは、LET文句を言います!次のコードの行:

let x = 
    compute { 
     let! a = add 10 20 
     let! b = add 11 2000 
     return a + b 
     } 

Error FS0001: This expression was expected to have type 
    'a * Effect list 
but here has type 
    Computation<'b> (FS0001) 

いずれかの提案がありますか?

+1

F# 'async'ワークフローは、すでにあなたの' sleep'機能を与え、あなたはWriterまたは状態を使用してロギングを実装することができモナド。これにより、あなたのワークフローを 'async'のスタックとして実装し、他の2つのモナドの1つとして実装することができます。 –

+2

私はそれを理解していますが、どのように動作するかを把握するために、最初からやりたいと思います。 – vidi

+0

Continuation CEを調べることに興味があるかもしれません。それは怠惰なCEを実装する一つの方法です。これは基本的に 'async'の実装方法です。 – FuleSnabel

答えて

2

定義が正しくない主なものは、計算ビルダーのメンバーの一部がComputation<'T>タイプを使用し、他のメンバーの一部が値と効果のリストのペアを直接使用することです。

タイプチェックを行うには、一貫性が必要です。次のバージョンはどこにでもComputation<'T>を使用しています - 例えば、Bindの型シグネチャを見て:

let private bind (Computation c) f : Computation<_> = 
    Computation(Lazy.Create(fun() -> 
     let u, effs = c.Value 
     let (Computation(c2)) = f u 
     let v, newEffs = c2.Value 
     let allEffects = List.append effs newEffs 
     v, allEffects)) 

type ComputeBuilder() = 
    member this.Zero() = Computation(lazy ((), [])) 
    member this.Return(x) = Computation(lazy (x, [])) 
    member this.ReturnFrom(c) = c 
    member this.Bind(x, f) = bind x f 
    member this.Delay(funcToDelay:_ -> Computation<_>) = 
     Computation(Lazy.Create(fun() -> 
     let (Computation(r)) = funcToDelay() 
     r.Value)) 
+0

私は例としてこのhttp://fsharpforfunandprofit.com/posts/computation-expressions-builder-part5/を使っていましたが、それは一貫していないので、私の混乱です。ありがとう! – vidi

+0

@vidi柔軟性のビットがあります - 「結合」、「実行」の第2引数で使用され、「遅延」によって返される「遅延型」は良い代替設計ですが、すべての整合性を維持する方が簡単です:-) –

関連する問題