Skip to content

Taskex stuff #239

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Draft
wants to merge 2 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions src/FSharp.Control.TaskSeq/FSharp.Control.TaskSeq.fsproj
Original file line number Diff line number Diff line change
Expand Up @@ -45,6 +45,7 @@ Generates optimized IL code through resumable state machines, and comes with a c
<Compile Include="DebugUtils.fs" />
<Compile Include="Utils.fsi" />
<Compile Include="Utils.fs" />
<Compile Include="TaskEx.Operators.fs" />
<Compile Include="TaskSeqBuilder.fsi" />
<Compile Include="TaskSeqBuilder.fs" />
<Compile Include="TaskSeqInternal.fs" />
Expand Down
188 changes: 188 additions & 0 deletions src/FSharp.Control.TaskSeq/TaskEx.Operators.fs
Original file line number Diff line number Diff line change
@@ -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"

[<Struct>]
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<bool>) = if leftOp then rightOp else ValueTask.False

static member (?<-)(TaskAndOperator, leftOp: ValueTask<bool>, 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<bool>) = if leftOp then ValueTask.ofTask rightOp else ValueTask.False

static member (?<-)(TaskAndOperator, leftOp: #Task<bool>, 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


[<Struct>]
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<bool>) =
// simple
if leftOp then ValueTask.True else rightOp

static member (?<-)(TaskOrOperator, leftOp: ValueTask<bool>, 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<bool>) = if leftOp then ValueTask.True else ValueTask.ofTask rightOp

static member (?<-)(TaskOrOperator, leftOp: #Task<bool>, 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

[<AutoOpen>]
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
2 changes: 2 additions & 0 deletions src/FSharp.Control.TaskSeq/Utils.fs
Original file line number Diff line number Diff line change
Expand Up @@ -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' }
Expand Down
5 changes: 5 additions & 0 deletions src/FSharp.Control.TaskSeq/Utils.fsi
Original file line number Diff line number Diff line change
Expand Up @@ -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<bool>

/// A successfully completed Task of boolean that has the value true.
val True: Task<bool>

/// Convert an Async<'T> into a Task<'T>
val inline ofAsync: async: Async<'T> -> Task<'T>
Expand Down