I want to return an HttpResponseMessage
, which implements IDisposable
, within a Result
(or any other discriminated union). However, since Result
itself is not IDisposable, I can't use!
it like I would for the disposable object itself. What do I do? Can I implement my own DU called DisposableResult
that implements IDisposable
itself?
Below is an example of what I mean. crawlStuff
asynchronously returns Result<HttpResponseMessage, string>
. I can't use!
the result, leading to a memory leak unless I manually release it.
open System.Net.Http
let crawlStuff (client : HttpClient) : Async<Result<HttpResponseMessage, string>> =
async {
// res is HttpResponseMessage which implements IDisposable
let! res = client.GetAsync("https://ifconfig.me") |> Async.AwaitTask
return
if res.IsSuccessStatusCode then
Ok res
else
Error "Something wrong"
}
async {
use client = new HttpClient()
// Memory leak because result it could carry HttpResponseMessage.
// I want to use! here, but can't because Result<> is not IDisposable
let! response = crawlStuff client
printfn "%A" response
} |> Async.RunSynchronously
I would've create wrapper around Result
, that will dispose underlying values:
let (|AsDisposable|) x =
match box x with
| :? IDisposable as dis -> ValueSome dis
| _ -> ValueNone
type ResultDisposer<'v, 'e> =
struct
val Result : Result<'v, 'e>
new res = { Result = res }
interface IDisposable with
member r.Dispose() =
match r.Result with
// | Ok (:? IDisposable as dis) // causes FS0008, so we have to hack
| Ok (AsDisposable (ValueSome dis))
| Error (AsDisposable (ValueSome dis)) -> dis.Dispose()
| _ -> ()
end
type Result<'a, 'b> with
member r.AsDisposable = new ResultDisposer<'a, 'b>(r)
And use it this way
async {
use client = new HttpClient()
let! response = crawlStuff client
use _ = response.AsDisposable
printfn "%A" response
} |> Async.RunSynchronously
This solution avoids need to rewrite existing code to DisposableResult
and avoids allocations when disposable value is reference type, like in case of HttpResponseMessage
. But decompilation shows that F# boxes ResultDisposer
, even though it shouldn't :(
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With