2

与えられた結果の型

type Result<'t> = OK of 't | Error of string 

これらの関数はすべて Async < Result<'t> > を返し、次のように組み合わされています。

let a = async { return Result.OK 1000 }
let b = async { return Result.Error "some message" }

let sum x y = 
    async {
        let! r1 = x
        match r1 with
        | Result.OK v1 -> 
             let! r2 = y
             match r2 with
             | Result.OK v2 -> return v1 + v2
             | Result.Error msg -> return Result.Error msg
        | Result.Error msg -> return Result.Error msg
    }

このコードは見栄えが悪いので、代わりに次のようにしたいと思います。

type Result = Ok of int | Error of string

type MyMonadBuilder() =
    member x.Bind (v,f) = 
        async { 
            let! r = v
            match r with
            | Ok r' -> return! f r'
            | Error msg -> return Error msg
        }

    member x.Return v = async {return Ok v }

    member x.Delay(f) = f()

let mymonad = MyMonadBuilder()
let runMyMonad = Async.RunSynchronously

let a = mymonad { return 10 }
let b = mymonad { return 20 }

let c = 
    mymonad { 
        return Result.Error "Some message"
        //??? The above doesn't work but how do I return a failure here?
    }

let d = 
    async {
        return Ok 1000
    } 
    //how to wrap this async with mymonad such that I can use it together with my other computation expressions?

let sum x y = 
    mymonad {
        let! v1 = x
        let! v2 = y
        return v1 + v2
    }

[<EntryPoint>]
let main argv = 
    let v = sum a b |> runMyMonad
    match v with
    | Ok v' -> printfn "Ok: %A" v'
    | Error msg -> printf "Error: %s" msg

    System.Console.Read() |> ignore
    0 

質問は次のとおりです。

  1. 関数 c が mymonad でエラーを返すようにするにはどうすればよいですか?
  2. async を mymonad でラップするように関数 d を作成するにはどうすればよいですか?
  3. Async と同様の方法でモナドをパラメータ化するにはどうすればよいですか?

...私が書くことができるように

let f (a:MyMonad<int>) (b:MyMonad<string>) = ...

アップデート:

また、いくつかの mymonad 操作を並行して実行し、結果の配列を見て、エラーと成功の内容を確認したいと考えています。このため、例外を使用することはお勧めできません。

また、質問3に関して、私が意味したのは、呼び出し元が非同期を扱っていることを知らない/気にしないように、型をパラメーター化して不透明にすることでした。私がモナドを書いた方法では、呼び出し元は常に Async.RunSynchronously を使用して mymonad 式を実行できます。

更新 2:

これまでのところ、次のようになりました。

  1. MyMonadBuilder の各メンバーに明示的な型を使用します
  2. ReturnFrom を MyMonadBuilder に追加しました。この関数を使用して Async< Result<'t> > をラップします
  3. エラー値で mymonad を作成する failwith のようなヘルパー関数を追加しました

コードは次のようになります。

type MyMonad<'t> = 't Result Async

type MyMonadBuilder() =
    member x.Bind<'t> (v,f) : MyMonad<'t>= 
        async { 
            let! r = v
            match r with
            | Ok r' -> return! f r'
            | Error msg -> return Error msg
        }

    member x.Return<'t> v  : MyMonad<'t> = async {return Ok v }
    member x.ReturnFrom<'t> v  : MyMonad<'t> = v

    member x.Delay(f) = f()

let failwith<'t> : string -> MyMonad<'t> = Result.Error >> async.Return

これは私の目的にはかなり良いようです。ありがとう!

4

2 に答える 2

3

私のExtCoreライブラリのasyncChoiceワークフローは既にこれを実装しています -- NuGet で利用できるので、プロジェクトへの参照を追加し、ソース ファイルで名前空間を開き、次のようなコードを書き始めるだけです。ExtCore.Control

open ExtCore.Control

let asyncDivide100By (x : int) =
    asyncChoice {
        if x = 0 then
            return! AsyncChoice.error "Cannot divide by zero."
        else
            return (100 / x)
    }

let divide100By (x : int) =
    let result =
        asyncDivide100By x
        |> Async.RunSynchronously

    match result with
    | Choice1Of2 result ->
        printfn "100 / %i = %i" x result
    | Choice2Of2 errorMsg ->
        printfn "An error occurred: %s" errorMsg


[<EntryPoint>]
let main argv =
    divide100By 10
    divide100By 1
    divide100By 0

    0   // Exit code

asyncChoice は、F# コア ライブラリの標準Async<'T>Choice<_,_>型を使用して構築されるため、互換性の問題は発生しません。

于 2013-08-20T00:40:10.090 に答える