7 people like it.
Like the snippet!
Computation Expression Stub
Stubbed methods for a computation expression with some explanations
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:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
84:
85:
86:
87:
88:
89:
90:
91:
92:
93:
94:
95:
96:
97:
98:
99:
100:
101:
102:
103:
104:
105:
106:
107:
108:
109:
110:
111:
112:
113:
114:
115:
116:
117:
118:
119:
120:
121:
122:
123:
124:
125:
126:
127:
128:
129:
130:
131:
132:
133:
134:
135:
136:
137:
138:
139:
140:
141:
142:
143:
144:
145:
146:
147:
148:
149:
150:
151:
152:
153:
154:
155:
156:
157:
158:
159:
160:
161:
162:
163:
164:
165:
166:
167:
168:
169:
170:
171:
172:
173:
174:
175:
176:
177:
178:
179:
180:
181:
182:
183:
184:
185:
186:
187:
188:
189:
190:
191:
192:
193:
194:
195:
196:
197:
198:
199:
200:
201:
202:
203:
204:
205:
206:
207:
208:
209:
210:
211:
212:
213:
214:
215:
216:
217:
218:
219:
220:
221:
222:
223:
|
open System
// Replace with your type
type MyType<'a> = Async<'a>
type Internal<'a> = MyType<'a>
// Replace with types that feel similar and can be converted to `MyType`
type External1<'a> = System.Threading.Tasks.Task<'a>
type External2<'a> = System.Threading.Tasks.ValueTask<'a>
/// https://fsharpforfunandprofit.com/posts/computation-expressions-builder-part3/
type Delayed<'a> = unit -> MyType<'a>
// What is InlineIfLambda? This allows generation of high performance code which Computation Expressions have had in the past.
// See https://github.com/fsharp/fslang-design/blob/main/FSharp-6.0/FS-1098-inline-if-lambda.md
// We also mark everything an inline to try to squeeze as much performance out of the implementation as possible.
/// https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/computation-expressions#creating-a-new-type-of-computation-expression
type StubBuilder() =
/// Called for let! and do! in computation expressions.
member inline this.Bind(input: Internal<'T>, [<InlineIfLambda>] f: ('T -> Internal<'U>)) : Internal<'U> =
raise <| NotImplementedException()
/// Called for efficient let! and and! in computation expressions without merging inputs.
member inline this.Bind2
(
input: Internal<'T1>,
input2: Internal<'T2>,
[<InlineIfLambda>] f: ('T1 * 'T2 -> Internal<'U>)
) : Internal<'U> =
raise <| NotImplementedException()
/// Called for efficient let! and and! in computation expressions without merging inputs.
member inline this.Bind3
(
input: Internal<'T1>,
input2: Internal<'T2>,
input3: Internal<'T3>,
[<InlineIfLambda>] f: ('T1 * 'T2 * 'T3 -> Internal<'U>)
) : Internal<'U> =
raise <| NotImplementedException()
/// Called for return in computation expressions.
member inline this.Return(input: 'T) : Internal<'T> = raise <| NotImplementedException()
/// Called for return! in computation expressions.
member inline this.ReturnFrom(input: Internal<'T>) : Internal<'T> = input
/// Called for an efficient let! ... return in computation expressions.
member inline this.BindReturn(input: Internal<'T>, [<InlineIfLambda>] f: ('T -> 'U)) : Internal<'U> =
raise <| NotImplementedException()
/// Called for efficient let! ... and! ... return in computation expressions without merging inputs.
member inline this.Bind2Return
(
input: Internal<'T1>,
input2: Internal<'T2>,
[<InlineIfLambda>] f: ('T1 * 'T2 -> 'U)
) : Internal<'U> =
raise <| NotImplementedException()
/// Called for efficient let! ... and! ... return in computation expressions without merging inputs.
member inline this.Bind3Return
(
input: Internal<'T1>,
input2: Internal<'T2>,
input3: Internal<'T3>,
[<InlineIfLambda>] f: ('T1 * 'T2 * 'T3 -> 'U)
) : Internal<'U> =
raise <| NotImplementedException()
/// Called for and! in computation expressions.
member inline this.MergeSources(input: Internal<'T1>, input2: Internal<'T2>) : Internal<'T1 * 'T2> =
raise <| NotImplementedException()
/// Called for and! in computation expressions, but improves efficiency by reducing the number of tupling nodes.
member inline this.MergeSources3
(
input: Internal<'T1>,
input2: Internal<'T2>,
input3: Internal<'T3>
) : Internal<'T1 * 'T2 * 'T3> =
raise <| NotImplementedException()
/// Wraps a computation expression as a function. Delayed<'T> can be any type, commonly Internal<'T> or unit -> Internal<'T> are used.
/// Many functions use the result of Delay as an argument: Run, While, TryWith, TryFinally, and Combine
member inline this.Delay([<InlineIfLambda>] f: unit -> Internal<'T>) : Delayed<'T> =
raise <| NotImplementedException()
/// Executes a computation expression.
member inline this.Run([<InlineIfLambda>] f: Delayed<'T>) : Internal<'T> = raise <| NotImplementedException()
/// Called for sequencing in computation expressions.
member inline this.Combine(input: Internal<'T>, [<InlineIfLambda>] f: Delayed<'T>) : Internal<'T> =
raise <| NotImplementedException()
/// Called for sequencing in computation expressions.
member inline this.Combine(input: Internal<unit>, f: Internal<'T>) : Internal<'T> = raise <| NotImplementedException()
/// Called for while...do expressions in computation expressions.
member inline this.While
(
[<InlineIfLambda>] guard: unit -> bool,
[<InlineIfLambda>] body: Delayed<'T>
) : Internal<'T> =
raise <| NotImplementedException()
/// Called for while...do expressions in computation expressions.
member inline this.While
(
[<InlineIfLambda>] guard: unit -> bool,
[<InlineIfLambda>] body: Delayed<unit>
) : Internal<unit> =
raise <| NotImplementedException()
/// Called for for...do expressions in computation expressions.
member inline this.For(xs: #seq<'T>, [<InlineIfLambda>] f: 'T -> Internal<'U>) : Internal<'U> =
raise <| NotImplementedException()
/// Called for for...do expressions in computation expressions.
// Only need one of these For implementations
// member inline this.For(xs : #seq<'T>, [<InlineIfLambda>] f : 'T -> Internal<'U> ) : seq<Internal<'U>> =
// raise <| NotImplementedException()
/// Called for try...finally expressions in computation expressions.
member inline this.TryFinally
(
[<InlineIfLambda>] body: Delayed<'T>,
[<InlineIfLambda>] final: unit -> unit
) : Internal<'T> =
raise <| NotImplementedException()
/// Called for try...finally expressions in computation expressions.
member inline this.TryWith
(
[<InlineIfLambda>] body: Delayed<'T>,
[<InlineIfLambda>] failure: exn -> Internal<'T>
) : Internal<'T> =
raise <| NotImplementedException()
/// Called for use bindings in computation expressions.
member inline this.Using(resource: 'T when 'T :> IAsyncDisposable, [<InlineIfLambda>] f: 'T -> Internal<'U>) : Internal<'U> =
// This may not need to be implemented for non async-like CEs and can be replaced by the Using in the Extensions module
raise <| NotImplementedException()
/// Called for empty else branches of if...then expressions in computation expressions.
member inline _.Zero() : Internal<'a> = raise <| NotImplementedException()
/// Called to convert an External to Internal type before any others calls in computation expression.
/// This is the identity function for the internal type
member inline _.Source(identity: Internal<'a>) : Internal<'a> = identity
/// Called to convert an External to Internal type before any others calls in computation expression.
/// This is the identity function for for loops.
member inline _.Source(identity: #seq<'a>) : #seq<'a> = identity
/// Called to convert an External to Internal type before any others calls in computation expression.
member inline _.Source(other: External1<Option<'a>>) : Internal<'a> = raise <| NotImplementedException()
/// Called to convert an External to Internal type before any others calls in computation expression.
member inline _.Source(other: External2<'a>) : Internal<'a> = raise <| NotImplementedException()
/// F#'s overloading resolution prioritizes extension methods lower
/// so if you have a more Derived type like `Async<Option<'T>>` vs `Async<'T>`, it won't know
/// which one to choose. You'll want to have the more specific in the Builder itself
/// and the less specific in an extension method.
///
/// See: https://github.com/fsharp/fslang-suggestions/issues/905
[<AutoOpen>]
module StubBuilderExtension =
let stub = StubBuilder()
type StubBuilder with
/// Called to convert an External to Internal type before any others calls in computation expression.
member inline _.Source(other: External1<'a>) : Internal<'a> = raise <| NotImplementedException()
/// Called for use bindings in computation expressions.
member inline this.Using(resource: 'T :> IDisposable, [<InlineIfLambda>] f: 'T -> Internal<'U>) : Internal<'U> =
// In extensions because of the priority resolution discussed above
// Otherwise you get a "Duplicate method. The method 'Using' has the same name and signature as another method in type 'StubBuilder'"
// Reason is, .NET cannot have overloaded methods based on constraints alone
// This can be moved to main builder if IAsyncDisposable is not required
raise <| NotImplementedException()
module Example =
open System.Threading.Tasks
let bindExamples () =
stub {
let! result = async { return "lol" } // string -> Used Normal Bind
let! result = task { return Some "lol" } // string -> Used Source member External1<Option<'a>>
let! result = task { return "lol" } // string -> Used Source member External1<'a> that is an extension member for lower binding resolution.
and! result4 = ValueTask<string>("lol") // string -> Used Source member Extenrnal2<'a>
return "lol"
}
type DisposableExample() =
interface IDisposable with
member this.Dispose() : unit = printfn "disposed"
let disposeExample () =
stub {
use d = new DisposableExample()
return "lol"
}
type AsyncDisposableExample() =
interface IAsyncDisposable with
member this.DisposeAsync() : ValueTask =
printfn "disposed"
ValueTask()
let asyncDisposeExample () =
stub {
use d = new AsyncDisposableExample()
return "lol"
}
|
namespace System
type MyType<'a> = Async<'a>
Multiple items
type Async =
static member AsBeginEnd : computation:('Arg -> Async<'T>) -> ('Arg * AsyncCallback * obj -> IAsyncResult) * (IAsyncResult -> 'T) * (IAsyncResult -> unit)
static member AwaitEvent : event:IEvent<'Del,'T> * ?cancelAction:(unit -> unit) -> Async<'T> (requires delegate and 'Del :> Delegate)
static member AwaitIAsyncResult : iar:IAsyncResult * ?millisecondsTimeout:int -> Async<bool>
static member AwaitTask : task:Task<'T> -> Async<'T> + 1 overload
static member AwaitWaitHandle : waitHandle:WaitHandle * ?millisecondsTimeout:int -> Async<bool>
static member CancelDefaultToken : unit -> unit
static member Catch : computation:Async<'T> -> Async<Choice<'T,exn>>
static member Choice : computations:seq<Async<'T option>> -> Async<'T option>
static member FromBeginEnd : beginAction:(AsyncCallback * obj -> IAsyncResult) * endAction:(IAsyncResult -> 'T) * ?cancelAction:(unit -> unit) -> Async<'T> + 3 overloads
static member FromContinuations : callback:(('T -> unit) * (exn -> unit) * (OperationCanceledException -> unit) -> unit) -> Async<'T>
...
--------------------
type Async<'T> =
Multiple items
namespace Internal
--------------------
type Internal<'a> = MyType<'a>
type External1<'a> = Threading.Tasks.Task<'a>
namespace System.Threading
namespace System.Threading.Tasks
Multiple items
type Task =
interface IAsyncResult
interface IDisposable
new : canceled: bool * creationOptions: TaskCreationOptions * ct: CancellationToken -> unit + 11 overloads
member AddCompletionAction : action: ITaskCompletionAction -> unit + 1 overload
member AddException : exceptionObject: obj -> unit + 1 overload
member AddExceptionsFromChildren : props: ContingentProperties -> unit
member AddNewChild : unit -> unit
member AddTaskContinuation : tc: obj * addBeforeOthers: bool -> bool
member AddTaskContinuationComplex : tc: obj * addBeforeOthers: bool -> bool
member AssignCancellationToken : cancellationToken: CancellationToken * antecedent: Task * continuation: TaskContinuation -> unit
...
--------------------
type Task<'TResult> =
inherit Task
new : unit -> unit + 13 overloads
member ConfigureAwait : continueOnCapturedContext: bool -> ConfiguredTaskAwaitable<'TResult>
member ContinueWith : continuationAction: Action<Task<'TResult>> -> Task + 23 overloads
member DangerousSetResult : result: 'TResult -> unit
member GetAwaiter : unit -> TaskAwaiter<'TResult>
member GetResultCore : waitCompletionNotification: bool -> 'TResult
member InnerInvoke : unit -> unit
member TrySetResult : result: 'TResult -> bool
static member StartNew : parent: Task * function: Func<'TResult> * cancellationToken: CancellationToken * creationOptions: TaskCreationOptions * internalOptions: InternalTaskOptions * scheduler: TaskScheduler -> Task<'TResult> + 1 overload
...
--------------------
Threading.Tasks.Task(action: Action) : Threading.Tasks.Task
Threading.Tasks.Task(action: Action, cancellationToken: Threading.CancellationToken) : Threading.Tasks.Task
Threading.Tasks.Task(action: Action, creationOptions: Threading.Tasks.TaskCreationOptions) : Threading.Tasks.Task
Threading.Tasks.Task(action: Action<obj>, state: obj) : Threading.Tasks.Task
Threading.Tasks.Task(action: Action, cancellationToken: Threading.CancellationToken, creationOptions: Threading.Tasks.TaskCreationOptions) : Threading.Tasks.Task
Threading.Tasks.Task(action: Action<obj>, state: obj, cancellationToken: Threading.CancellationToken) : Threading.Tasks.Task
Threading.Tasks.Task(action: Action<obj>, state: obj, creationOptions: Threading.Tasks.TaskCreationOptions) : Threading.Tasks.Task
Threading.Tasks.Task(action: Action<obj>, state: obj, cancellationToken: Threading.CancellationToken, creationOptions: Threading.Tasks.TaskCreationOptions) : Threading.Tasks.Task
--------------------
Threading.Tasks.Task(function: Func<'TResult>) : Threading.Tasks.Task<'TResult>
Threading.Tasks.Task(function: Func<'TResult>, cancellationToken: Threading.CancellationToken) : Threading.Tasks.Task<'TResult>
Threading.Tasks.Task(function: Func<'TResult>, creationOptions: Threading.Tasks.TaskCreationOptions) : Threading.Tasks.Task<'TResult>
Threading.Tasks.Task(function: Func<obj,'TResult>, state: obj) : Threading.Tasks.Task<'TResult>
Threading.Tasks.Task(function: Func<'TResult>, cancellationToken: Threading.CancellationToken, creationOptions: Threading.Tasks.TaskCreationOptions) : Threading.Tasks.Task<'TResult>
Threading.Tasks.Task(function: Func<obj,'TResult>, state: obj, cancellationToken: Threading.CancellationToken) : Threading.Tasks.Task<'TResult>
Threading.Tasks.Task(function: Func<obj,'TResult>, state: obj, creationOptions: Threading.Tasks.TaskCreationOptions) : Threading.Tasks.Task<'TResult>
Threading.Tasks.Task(function: Func<obj,'TResult>, state: obj, cancellationToken: Threading.CancellationToken, creationOptions: Threading.Tasks.TaskCreationOptions) : Threading.Tasks.Task<'TResult>
[<Struct>]
type External2<'a> = Threading.Tasks.ValueTask<'a>
Multiple items
[<Struct>]
type ValueTask<'TResult> =
new : result: 'TResult -> unit + 3 overloads
member AsTask : unit -> Task<'TResult>
member ConfigureAwait : continueOnCapturedContext: bool -> ConfiguredValueTaskAwaitable<'TResult>
member Equals : obj: obj -> bool + 1 overload
member GetAwaiter : unit -> ValueTaskAwaiter<'TResult>
member GetHashCode : unit -> int
member GetTaskForValueTaskSource : t: IValueTaskSource<'TResult> -> Task<'TResult>
member Preserve : unit -> ValueTask<'TResult>
member ToString : unit -> string
static member op_Equality : left: ValueTask<'TResult> * right: ValueTask<'TResult> -> bool
...
--------------------
[<Struct>]
type ValueTask =
new : task: Task -> unit + 2 overloads
member AsTask : unit -> Task
member ConfigureAwait : continueOnCapturedContext: bool -> ConfiguredValueTaskAwaitable
member Equals : obj: obj -> bool + 1 overload
member GetAwaiter : unit -> ValueTaskAwaiter
member GetHashCode : unit -> int
member GetTaskForValueTaskSource : t: IValueTaskSource -> Task
member Preserve : unit -> ValueTask
member ThrowIfCompletedUnsuccessfully : unit -> unit
static member op_Equality : left: ValueTask * right: ValueTask -> bool
...
--------------------
Threading.Tasks.ValueTask ()
Threading.Tasks.ValueTask(result: 'TResult) : Threading.Tasks.ValueTask<'TResult>
Threading.Tasks.ValueTask(task: Threading.Tasks.Task<'TResult>) : Threading.Tasks.ValueTask<'TResult>
Threading.Tasks.ValueTask(source: Threading.Tasks.Sources.IValueTaskSource<'TResult>, token: int16) : Threading.Tasks.ValueTask<'TResult>
--------------------
Threading.Tasks.ValueTask ()
Threading.Tasks.ValueTask(task: Threading.Tasks.Task) : Threading.Tasks.ValueTask
Threading.Tasks.ValueTask(source: Threading.Tasks.Sources.IValueTaskSource, token: int16) : Threading.Tasks.ValueTask
type Delayed<'a> = unit -> MyType<'a>
https://fsharpforfunandprofit.com/posts/computation-expressions-builder-part3/
type unit = Unit
Multiple items
type StubBuilder =
new : unit -> StubBuilder
member Bind : input:Internal<'T> * f:('T -> Internal<'U>) -> Internal<'U>
member Bind2 : input:Internal<'T1> * input2:Internal<'T2> * f:('T1 * 'T2 -> Internal<'U>) -> Internal<'U>
member Bind2Return : input:Internal<'T1> * input2:Internal<'T2> * f:('T1 * 'T2 -> 'U) -> Internal<'U>
member Bind3 : input:Internal<'T1> * input2:Internal<'T2> * input3:Internal<'T3> * f:('T1 * 'T2 * 'T3 -> Internal<'U>) -> Internal<'U>
member Bind3Return : input:Internal<'T1> * input2:Internal<'T2> * input3:Internal<'T3> * f:('T1 * 'T2 * 'T3 -> 'U) -> Internal<'U>
member BindReturn : input:Internal<'T> * f:('T -> 'U) -> Internal<'U>
member Combine : input:Internal<unit> * f:Internal<'T> -> Internal<'T> + 1 overload
member Delay : f:(unit -> Internal<'T>) -> Delayed<'T>
member For : xs:#seq<'T> * f:('T -> Internal<'U>) -> Internal<'U>
...
https://learn.microsoft.com/en-us/dotnet/fsharp/language-reference/computation-expressions#creating-a-new-type-of-computation-expression
--------------------
new : unit -> StubBuilder
val this : StubBuilder
val input : Internal<'T>
val f : ('T -> Internal<'U>)
val raise : exn:Exception -> 'T
Multiple items
type NotImplementedException =
inherit SystemException
new : unit -> unit + 3 overloads
static val InnerExceptionPrefix : string
static val s_DispatchStateLock : obj
--------------------
NotImplementedException() : NotImplementedException
NotImplementedException(message: string) : NotImplementedException
NotImplementedException(message: string, inner: exn) : NotImplementedException
val input : Internal<'T1>
val input2 : Internal<'T2>
val f : ('T1 * 'T2 -> Internal<'U>)
val input3 : Internal<'T3>
val f : ('T1 * 'T2 * 'T3 -> Internal<'U>)
val input : 'T
val f : ('T -> 'U)
val f : ('T1 * 'T2 -> 'U)
val f : ('T1 * 'T2 * 'T3 -> 'U)
val f : (unit -> Internal<'T>)
val f : Delayed<'T>
val input : Internal<unit>
val f : Internal<'T>
val guard : (unit -> bool)
[<Struct>]
type bool = Boolean
val body : Delayed<'T>
val body : Delayed<unit>
val xs : #seq<'T>
Multiple items
val seq : sequence:seq<'T> -> seq<'T>
--------------------
type seq<'T> = Collections.Generic.IEnumerable<'T>
val final : (unit -> unit)
val failure : (exn -> Internal<'T>)
type exn = Exception
val resource : #IAsyncDisposable
type IAsyncDisposable =
member DisposeAsync : unit -> ValueTask
val f : (#IAsyncDisposable -> Internal<'U>)
val identity : Internal<'a>
val identity : #seq<'a0>
val other : External1<Option<'a>>
module Option
from Microsoft.FSharp.Core
val other : External2<'a>
Multiple items
type AutoOpenAttribute =
inherit Attribute
new : unit -> AutoOpenAttribute + 1 overload
member Path : string
--------------------
new : unit -> AutoOpenAttribute
new : path:string -> AutoOpenAttribute
val stub : StubBuilder
val other : External1<'a>
val resource : #IDisposable
type IDisposable =
member Dispose : unit -> unit
val f : (#IDisposable -> Internal<'U>)
module Example
from Script
val bindExamples : unit -> Internal<string>
val result : string
val async : AsyncBuilder
val result : obj
union case Option.Some: Value: 'T -> Option<'T>
val result4 : string
Multiple items
[<Struct>]
type ValueTask =
new : task: Task -> unit + 2 overloads
member AsTask : unit -> Task
member ConfigureAwait : continueOnCapturedContext: bool -> ConfiguredValueTaskAwaitable
member Equals : obj: obj -> bool + 1 overload
member GetAwaiter : unit -> ValueTaskAwaiter
member GetHashCode : unit -> int
member GetTaskForValueTaskSource : t: IValueTaskSource -> Task
member Preserve : unit -> ValueTask
member ThrowIfCompletedUnsuccessfully : unit -> unit
static member op_Equality : left: ValueTask * right: ValueTask -> bool
...
--------------------
[<Struct>]
type ValueTask<'TResult> =
new : result: 'TResult -> unit + 3 overloads
member AsTask : unit -> Task<'TResult>
member ConfigureAwait : continueOnCapturedContext: bool -> ConfiguredValueTaskAwaitable<'TResult>
member Equals : obj: obj -> bool + 1 overload
member GetAwaiter : unit -> ValueTaskAwaiter<'TResult>
member GetHashCode : unit -> int
member GetTaskForValueTaskSource : t: IValueTaskSource<'TResult> -> Task<'TResult>
member Preserve : unit -> ValueTask<'TResult>
member ToString : unit -> string
static member op_Equality : left: ValueTask<'TResult> * right: ValueTask<'TResult> -> bool
...
--------------------
ValueTask ()
ValueTask(task: Task) : ValueTask
ValueTask(source: Sources.IValueTaskSource, token: int16) : ValueTask
--------------------
ValueTask ()
ValueTask(result: 'TResult) : ValueTask<'TResult>
ValueTask(task: Task<'TResult>) : ValueTask<'TResult>
ValueTask(source: Sources.IValueTaskSource<'TResult>, token: int16) : ValueTask<'TResult>
Multiple items
val string : value:'T -> string
--------------------
type string = String
Multiple items
type DisposableExample =
interface IDisposable
new : unit -> DisposableExample
--------------------
new : unit -> DisposableExample
val this : DisposableExample
val printfn : format:Printf.TextWriterFormat<'T> -> 'T
val disposeExample : unit -> Internal<string>
val d : DisposableExample
Multiple items
type AsyncDisposableExample =
interface IAsyncDisposable
new : unit -> AsyncDisposableExample
--------------------
new : unit -> AsyncDisposableExample
val this : AsyncDisposableExample
val asyncDisposeExample : unit -> Internal<string>
val d : AsyncDisposableExample
More information