3 people like it.

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)
Next Version Raw view Test code New version

More information

Link:http://fssnip.net/87q
Posted:5 months ago
Author:Crazy Monk
Tags: computational workflows , computation builder , monad