diff --git a/src/FSharp.Control.TaskSeq/FSharp.Control.TaskSeq.fsproj b/src/FSharp.Control.TaskSeq/FSharp.Control.TaskSeq.fsproj
index 15cfdc6..9e51f8e 100644
--- a/src/FSharp.Control.TaskSeq/FSharp.Control.TaskSeq.fsproj
+++ b/src/FSharp.Control.TaskSeq/FSharp.Control.TaskSeq.fsproj
@@ -45,6 +45,7 @@ Generates optimized IL code through resumable state machines, and comes with a c
+
diff --git a/src/FSharp.Control.TaskSeq/TaskEx.Operators.fs b/src/FSharp.Control.TaskSeq/TaskEx.Operators.fs
new file mode 100644
index 0000000..b8cac92
--- /dev/null
+++ b/src/FSharp.Control.TaskSeq/TaskEx.Operators.fs
@@ -0,0 +1,188 @@
+namespace FSharp.Control.Operators
+
+open System.Threading.Tasks
+open System
+
+open FSharp.Control
+
+// "The '&&' should not normally be redefined. Consider using a different operator name.
+// "The '||' should not normally be redefined. Consider using a different operator name.
+#nowarn "86"
+
+[]
+type TaskAndOperator =
+ | TaskAndOperator
+
+ // original. In release builds, this is optimized just like the original '&&' operator and the SCDU disappears
+ static member inline (?<-)(_: TaskAndOperator, leftOp: bool, rightOp: bool) = leftOp && rightOp
+
+ static member (?<-)(TaskAndOperator, leftOp: bool, rightOp: ValueTask) = if leftOp then rightOp else ValueTask.False
+
+ static member (?<-)(TaskAndOperator, leftOp: ValueTask, rightOp: bool) =
+ // while it may be more efficient to evaluate rh-side first, we should honor order of operations!
+ if leftOp.IsCompletedSuccessfully && leftOp.Result then
+ ValueTask.fromResult rightOp
+ else
+ task {
+ let! leftOperand = leftOp
+ if leftOperand then return rightOp else return false
+ }
+ |> ValueTask.ofTask
+
+ static member (?<-)(TaskAndOperator, leftOp: bool, rightOp: #Task) = if leftOp then ValueTask.ofTask rightOp else ValueTask.False
+
+ static member (?<-)(TaskAndOperator, leftOp: #Task, rightOp: bool) =
+ // while it may be more efficient to evaluate rh-side first, we should honor order of operations!
+ if leftOp.IsCompletedSuccessfully then
+ if leftOp.Result then
+ ValueTask.fromResult rightOp
+ else
+ ValueTask.False
+ else
+ task {
+ let! leftOperand = leftOp
+ if leftOperand then return rightOp else return false
+ }
+ |> ValueTask.ofTask
+
+ static member (?<-)(TaskAndOperator, leftOp: ValueTask<_>, rightOp: ValueTask<_>) =
+ if leftOp.IsCompletedSuccessfully then
+ if leftOp.Result then rightOp else ValueTask.False
+ else
+ task {
+ let! leftOperand = leftOp
+ if leftOperand then return! rightOp else return false
+ }
+ |> ValueTask.ofTask
+
+ static member (?<-)(TaskAndOperator, leftOp: #Task<_>, rightOp: ValueTask<_>) =
+ if leftOp.IsCompletedSuccessfully then
+ if leftOp.Result then rightOp else ValueTask.False
+ else
+ task {
+ let! leftOperand = leftOp
+ if leftOperand then return! rightOp else return false
+ }
+ |> ValueTask.ofTask
+
+ static member (?<-)(TaskAndOperator, leftOp: ValueTask<_>, rightOp: #Task<_>) =
+ if leftOp.IsCompletedSuccessfully then
+ if leftOp.Result then
+ ValueTask.ofTask rightOp
+ else
+ ValueTask.False
+ else
+ task {
+ let! leftOperand = leftOp
+ if leftOperand then return! rightOp else return false
+ }
+ |> ValueTask.ofTask
+
+ static member (?<-)(_: TaskAndOperator, leftOp: #Task<_>, rightOp: #Task<_>) =
+ if leftOp.IsCompletedSuccessfully then
+ if leftOp.Result then
+ ValueTask.ofTask rightOp
+ else
+ ValueTask.False
+ else
+ task {
+ let! leftOperand = leftOp
+ if not leftOperand then return false else return! rightOp
+ }
+ |> ValueTask.ofTask
+
+
+[]
+type TaskOrOperator =
+ | TaskOrOperator
+
+ // original. In release builds, this is optimized just like the original '||' operator and the SCDU disappears
+ static member inline (?<-)(_, leftOp: bool, rightOp: bool) = leftOp || rightOp
+
+ static member (?<-)(TaskOrOperator, leftOp: bool, rightOp: ValueTask) =
+ // simple
+ if leftOp then ValueTask.True else rightOp
+
+ static member (?<-)(TaskOrOperator, leftOp: ValueTask, rightOp: bool) =
+ if leftOp.IsCompletedSuccessfully then
+ if leftOp.Result then
+ ValueTask.True
+ else
+ ValueTask.fromResult rightOp
+ else
+ task {
+ let! leftOperand = leftOp
+ return leftOperand || rightOp
+ }
+ |> ValueTask.ofTask
+
+ static member (?<-)(TaskOrOperator, leftOp: bool, rightOp: #Task) = if leftOp then ValueTask.True else ValueTask.ofTask rightOp
+
+ static member (?<-)(TaskOrOperator, leftOp: #Task, rightOp: bool) =
+ // while it may be more efficient to evaluate rh-side first, we should honor order of operations!
+ if leftOp.IsCompletedSuccessfully then
+ if leftOp.Result then
+ ValueTask.True
+ else
+ ValueTask.fromResult rightOp
+ else
+ task {
+ let! leftOperand = leftOp
+ return leftOperand || rightOp
+ }
+ |> ValueTask.ofTask
+
+ static member (?<-)(TaskOrOperator, leftOp: ValueTask<_>, rightOp: ValueTask<_>) =
+ if leftOp.IsCompletedSuccessfully then
+ if leftOp.Result then ValueTask.True else rightOp
+ else
+ task {
+ let! leftOperand = leftOp
+ if leftOperand then return true else return! rightOp
+ }
+ |> ValueTask.ofTask
+
+ static member (?<-)(TaskOrOperator, leftOp: #Task<_>, rightOp: ValueTask<_>) =
+ if leftOp.IsCompletedSuccessfully then
+ if leftOp.Result then ValueTask.True else rightOp
+ else
+ task {
+ let! leftOperand = leftOp
+ if leftOperand then return true else return! rightOp
+ }
+ |> ValueTask.ofTask
+
+ static member (?<-)(TaskOrOperator, leftOp: ValueTask<_>, rightOp: #Task<_>) =
+ if leftOp.IsCompletedSuccessfully then
+ if leftOp.Result then
+ ValueTask.True
+ else
+ ValueTask.ofTask rightOp
+ else
+ task {
+ let! leftOperand = leftOp
+ if leftOperand then return true else return! rightOp
+ }
+ |> ValueTask.ofTask
+
+ static member (?<-)(TaskOrOperator, leftOp: #Task<_>, rightOp: #Task<_>) =
+ if leftOp.IsCompletedSuccessfully then
+ if leftOp.Result then
+ ValueTask.True
+ else
+ ValueTask.ofTask rightOp
+ else
+ task {
+ let! leftOperand = leftOp
+ if leftOperand then return true else return! rightOp
+ }
+ |> ValueTask.ofTask
+
+[]
+module OperatorOverloads =
+
+ /// Binary 'and'. When used as a binary operator, the right-hand operand is evaluated only on demand.
+ let inline (&&) leftOp rightOp : 'T = ((?<-) TaskAndOperator leftOp rightOp) // SCDU will get erased in release builds
+
+ /// Binary 'or'. When used as a binary operator, the right-hand operand is evaluated only on demand.
+ let inline (||) leftOp rightOp : 'T = ((?<-) TaskOrOperator leftOp rightOp) // SCDU will get erased in release builds
diff --git a/src/FSharp.Control.TaskSeq/Utils.fs b/src/FSharp.Control.TaskSeq/Utils.fs
index c02bab3..687e4c0 100644
--- a/src/FSharp.Control.TaskSeq/Utils.fs
+++ b/src/FSharp.Control.TaskSeq/Utils.fs
@@ -42,6 +42,8 @@ module ValueTask =
module Task =
+ let False = Task.FromResult false
+ let True = Task.FromResult true
let inline fromResult (value: 'U) : Task<'U> = Task.FromResult value
let inline ofAsync (async: Async<'T>) = task { return! async }
let inline ofTask (task': Task) = task { do! task' }
diff --git a/src/FSharp.Control.TaskSeq/Utils.fsi b/src/FSharp.Control.TaskSeq/Utils.fsi
index d34a1e5..74a3ba2 100644
--- a/src/FSharp.Control.TaskSeq/Utils.fsi
+++ b/src/FSharp.Control.TaskSeq/Utils.fsi
@@ -49,6 +49,11 @@ module ValueTask =
val inline ignore: vtask: ValueTask<'T> -> ValueTask
module Task =
+ /// A successfully completed Task of boolean that has the value false.
+ val False: Task
+
+ /// A successfully completed Task of boolean that has the value true.
+ val True: Task
/// Convert an Async<'T> into a Task<'T>
val inline ofAsync: async: Async<'T> -> Task<'T>