3 people like it.
Like the snippet!
Disposable Scopes provided by scope {...} computational workflow.
This snippet provides a builder for scope {...} computational workflow that returns a Scope object implementing System.IDisposable interface.
All "use" bindings inside scope {...} workflow are kept from being disposed immediately when computation leaves scope. Instead, they are disposed only when resulting Scope object is disposed. This makes possible to access disposable outside the scope while it remains usable. This can be achieved by via return statement of workflow that can return, for example, a closure that encapsulates disposable.
scope {...} return value is available through Value property of Scope object. Scopes may be nested through let! binding that means that nested scope will be disposed when the resulting scope is disposed.
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
|
open System
type Stack = Collections.Generic.Queue<IDisposable>
type Scope<'a> (value:'a) =
let stack = Stack()
member _.Value = value
member internal _.Stack = stack
member _.Close () =
for i in stack do
try
i.Dispose()
with
_ -> ()
interface IDisposable with
member this.Dispose () = this.Close()
module Internals =
let getStack (x:Scope<_>) = x.Stack
type ScopeBuilder () =
member _. Return x = new Scope<_>(x)
member _.Using (res ,body) =
let mutable disposeNow = true
try
let (x:Scope<_>) = body res
disposeNow <- false
x.Stack.Enqueue res
x
finally
if disposeNow then (res :> IDisposable).Dispose()
let scope = ScopeBuilder ()
// example
open System.IO
let somescope =
scope {
use ms = new MemoryStream()
use sw = new StreamWriter(ms)
sw.Write "this
is
multiline
text"
sw.Flush()
ms.Seek (0L,SeekOrigin.Begin) |> ignore
use sr = new StreamReader(ms)
return
{|
Next = sr.ReadLine
|}
}
somescope .Value.Next() // reads text line by line
somescope .Close() // try execute above command now - you'll get an error
|
namespace System
type Stack = Collections.Generic.Queue<IDisposable>
Multiple items
namespace System.Collections
--------------------
namespace Microsoft.FSharp.Collections
namespace System.Collections.Generic
Multiple items
type Queue<'T> =
new : unit -> Queue<'T> + 2 overloads
member Clear : unit -> unit
member Contains : item:'T -> bool
member CopyTo : array:'T[] * arrayIndex:int -> unit
member Count : int
member Dequeue : unit -> 'T
member Enqueue : item:'T -> unit
member GetEnumerator : unit -> Enumerator<'T>
member Peek : unit -> 'T
member ToArray : unit -> 'T[]
...
nested type Enumerator
--------------------
Collections.Generic.Queue() : Collections.Generic.Queue<'T>
Collections.Generic.Queue(capacity: int) : Collections.Generic.Queue<'T>
Collections.Generic.Queue(collection: Collections.Generic.IEnumerable<'T>) : Collections.Generic.Queue<'T>
type IDisposable =
member Dispose : unit -> unit
Multiple items
type Scope<'a> =
interface IDisposable
new : value:'a -> Scope<'a>
member Close : unit -> unit
member internal Stack : Stack
member Value : 'a
--------------------
new : value:'a -> Scope<'a>
val value : 'a
val stack : Stack
val i : IDisposable
val this : Scope<'a>
val getStack : x:Scope<'a> -> Stack
val x : Scope<'a>
Multiple items
type ScopeBuilder =
new : unit -> ScopeBuilder
member Return : x:'c -> Scope<'c>
member Using : res:'a * body:('a -> Scope<'b>) -> Scope<'b> (requires 'a :> IDisposable)
--------------------
new : unit -> ScopeBuilder
val x : 'c
val res : #IDisposable
val body : (#IDisposable -> Scope<'b>)
val mutable disposeNow : bool
val x : Scope<'b>
val scope : ScopeBuilder
namespace System.IO
val somescope : Scope<{| Next: (unit -> string) |}>
val ms : MemoryStream
Multiple items
type MemoryStream =
inherit Stream
new : unit -> MemoryStream + 6 overloads
member CanRead : bool
member CanSeek : bool
member CanWrite : bool
member Capacity : int with get, set
member CopyTo : destination:Stream * bufferSize:int -> unit
member CopyToAsync : destination:Stream * bufferSize:int * cancellationToken:CancellationToken -> Task
member Flush : unit -> unit
member FlushAsync : cancellationToken:CancellationToken -> Task
member GetBuffer : unit -> byte[]
...
--------------------
MemoryStream() : MemoryStream
MemoryStream(capacity: int) : MemoryStream
MemoryStream(buffer: byte []) : MemoryStream
MemoryStream(buffer: byte [], writable: bool) : MemoryStream
MemoryStream(buffer: byte [], index: int, count: int) : MemoryStream
MemoryStream(buffer: byte [], index: int, count: int, writable: bool) : MemoryStream
MemoryStream(buffer: byte [], index: int, count: int, writable: bool, publiclyVisible: bool) : MemoryStream
val sw : StreamWriter
Multiple items
type StreamWriter =
inherit TextWriter
new : stream:Stream -> StreamWriter + 7 overloads
member AutoFlush : bool with get, set
member BaseStream : Stream
member Close : unit -> unit
member DisposeAsync : unit -> ValueTask
member Encoding : Encoding
member Flush : unit -> unit
member FlushAsync : unit -> Task
member Write : value:char -> unit + 8 overloads
member WriteAsync : value:char -> Task + 3 overloads
...
--------------------
StreamWriter(stream: Stream) : StreamWriter
StreamWriter(path: string) : StreamWriter
StreamWriter(stream: Stream, encoding: Text.Encoding) : StreamWriter
StreamWriter(path: string, append: bool) : StreamWriter
StreamWriter(stream: Stream, encoding: Text.Encoding, bufferSize: int) : StreamWriter
StreamWriter(path: string, append: bool, encoding: Text.Encoding) : StreamWriter
StreamWriter(stream: Stream,?encoding: Text.Encoding,?bufferSize: int,?leaveOpen: bool) : StreamWriter
StreamWriter(path: string, append: bool, encoding: Text.Encoding, bufferSize: int) : StreamWriter
type SeekOrigin =
| Begin = 0
| Current = 1
| End = 2
field SeekOrigin.Begin: SeekOrigin = 0
val ignore : value:'T -> unit
val sr : StreamReader
Multiple items
type StreamReader =
inherit TextReader
new : stream:Stream -> StreamReader + 10 overloads
member BaseStream : Stream
member Close : unit -> unit
member CurrentEncoding : Encoding
member DiscardBufferedData : unit -> unit
member EndOfStream : bool
member Peek : unit -> int
member Read : unit -> int + 2 overloads
member ReadAsync : buffer:Memory<char> * ?cancellationToken:CancellationToken -> ValueTask<int> + 1 overload
member ReadBlock : buffer:Span<char> -> int + 1 overload
...
--------------------
StreamReader(stream: Stream) : StreamReader
(+0 other overloads)
StreamReader(path: string) : StreamReader
(+0 other overloads)
StreamReader(stream: Stream, detectEncodingFromByteOrderMarks: bool) : StreamReader
(+0 other overloads)
StreamReader(stream: Stream, encoding: Text.Encoding) : StreamReader
(+0 other overloads)
StreamReader(path: string, detectEncodingFromByteOrderMarks: bool) : StreamReader
(+0 other overloads)
StreamReader(path: string, encoding: Text.Encoding) : StreamReader
(+0 other overloads)
StreamReader(stream: Stream, encoding: Text.Encoding, detectEncodingFromByteOrderMarks: bool) : StreamReader
(+0 other overloads)
StreamReader(path: string, encoding: Text.Encoding, detectEncodingFromByteOrderMarks: bool) : StreamReader
(+0 other overloads)
StreamReader(stream: Stream, encoding: Text.Encoding, detectEncodingFromByteOrderMarks: bool, bufferSize: int) : StreamReader
(+0 other overloads)
StreamReader(path: string, encoding: Text.Encoding, detectEncodingFromByteOrderMarks: bool, bufferSize: int) : StreamReader
(+0 other overloads)
More information