Given the result type
type Result<'t> = OK of 't | Error of string
I have these functions which all return Async < Result<'t> > which are combined something like this:
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
}
This code looks bad so instead I would like to have this:
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
So the questions are:
- How do I write function c such that it returns an error in mymonad?
- How do I write function d such that it wraps an async with a mymonad?
- How can I make my monad parameterized in a similar way Async is?
...such that I can write
let f (a:MyMonad<int>) (b:MyMonad<string>) = ...
UPDATE:
Also I would like to run several mymonad operations in parallel and then look at the array of results to see what were the errors and the successes. For this reason I think using exceptions is not a good idea.
Also, regarding the question 3, what I meant was to have my type parameterized and opaque such that the callers don't know/don't care they are dealing with an async. The way I wrote the monad the caller can always use Async.RunSynchronously to run a mymonad expression.
UPDATE 2:
So far I ended up with the following:
- I use an explicit type for each member of MyMonadBuilder
- I added ReturnFrom to the MyMonadBuilder. I use this function to wrap an Async< Result<'t> >
- I added helper functions like failwith which create a mymonad with the error value
The code looks like this:
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
This looks reasonably good for my purpose. Thanks!