Skip to content

Commit 263d42b

Browse files
committed
TaskEx: add && and || operators
1 parent 2fc7d94 commit 263d42b

File tree

2 files changed

+189
-0
lines changed

2 files changed

+189
-0
lines changed

src/FSharp.Control.TaskSeq/FSharp.Control.TaskSeq.fsproj

+1
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ Generates optimized IL code through resumable state machines, and comes with a c
4545
<Compile Include="DebugUtils.fs" />
4646
<Compile Include="Utils.fsi" />
4747
<Compile Include="Utils.fs" />
48+
<Compile Include="TaskEx.Operators.fs" />
4849
<Compile Include="TaskSeqBuilder.fsi" />
4950
<Compile Include="TaskSeqBuilder.fs" />
5051
<Compile Include="TaskSeqInternal.fs" />
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
namespace FSharp.Control.Operators
2+
3+
open System.Threading.Tasks
4+
open System
5+
6+
open FSharp.Control
7+
8+
// "The '&&' should not normally be redefined. Consider using a different operator name.
9+
// "The '||' should not normally be redefined. Consider using a different operator name.
10+
#nowarn "86"
11+
12+
[<Struct>]
13+
type TaskAndOperator =
14+
| TaskAndOperator
15+
16+
// original. In release builds, this is optimized just like the original '&&' operator and the SCDU disappears
17+
static member inline (?<-)(_: TaskAndOperator, leftOp: bool, rightOp: bool) = leftOp && rightOp
18+
19+
static member (?<-)(TaskAndOperator, leftOp: bool, rightOp: ValueTask<bool>) = if leftOp then rightOp else ValueTask.False
20+
21+
static member (?<-)(TaskAndOperator, leftOp: ValueTask<bool>, rightOp: bool) =
22+
// while it may be more efficient to evaluate rh-side first, we should honor order of operations!
23+
if leftOp.IsCompletedSuccessfully && leftOp.Result then
24+
ValueTask.fromResult rightOp
25+
else
26+
task {
27+
let! leftOperand = leftOp
28+
if leftOperand then return rightOp else return false
29+
}
30+
|> ValueTask.ofTask
31+
32+
static member (?<-)(TaskAndOperator, leftOp: bool, rightOp: #Task<bool>) = if leftOp then ValueTask.ofTask rightOp else ValueTask.False
33+
34+
static member (?<-)(TaskAndOperator, leftOp: #Task<bool>, rightOp: bool) =
35+
// while it may be more efficient to evaluate rh-side first, we should honor order of operations!
36+
if leftOp.IsCompletedSuccessfully then
37+
if leftOp.Result then
38+
ValueTask.fromResult rightOp
39+
else
40+
ValueTask.False
41+
else
42+
task {
43+
let! leftOperand = leftOp
44+
if leftOperand then return rightOp else return false
45+
}
46+
|> ValueTask.ofTask
47+
48+
static member (?<-)(TaskAndOperator, leftOp: ValueTask<_>, rightOp: ValueTask<_>) =
49+
if leftOp.IsCompletedSuccessfully then
50+
if leftOp.Result then rightOp else ValueTask.False
51+
else
52+
task {
53+
let! leftOperand = leftOp
54+
if leftOperand then return! rightOp else return false
55+
}
56+
|> ValueTask.ofTask
57+
58+
static member (?<-)(TaskAndOperator, leftOp: #Task<_>, rightOp: ValueTask<_>) =
59+
if leftOp.IsCompletedSuccessfully then
60+
if leftOp.Result then rightOp else ValueTask.False
61+
else
62+
task {
63+
let! leftOperand = leftOp
64+
if leftOperand then return! rightOp else return false
65+
}
66+
|> ValueTask.ofTask
67+
68+
static member (?<-)(TaskAndOperator, leftOp: ValueTask<_>, rightOp: #Task<_>) =
69+
if leftOp.IsCompletedSuccessfully then
70+
if leftOp.Result then
71+
ValueTask.ofTask rightOp
72+
else
73+
ValueTask.False
74+
else
75+
task {
76+
let! leftOperand = leftOp
77+
if leftOperand then return! rightOp else return false
78+
}
79+
|> ValueTask.ofTask
80+
81+
static member (?<-)(_: TaskAndOperator, leftOp: #Task<_>, rightOp: #Task<_>) =
82+
if leftOp.IsCompletedSuccessfully then
83+
if leftOp.Result then
84+
ValueTask.ofTask rightOp
85+
else
86+
ValueTask.False
87+
else
88+
task {
89+
let! leftOperand = leftOp
90+
if not leftOperand then return false else return! rightOp
91+
}
92+
|> ValueTask.ofTask
93+
94+
95+
[<Struct>]
96+
type TaskOrOperator =
97+
| TaskOrOperator
98+
99+
// original. In release builds, this is optimized just like the original '||' operator and the SCDU disappears
100+
static member inline (?<-)(_, leftOp: bool, rightOp: bool) = leftOp || rightOp
101+
102+
static member (?<-)(TaskOrOperator, leftOp: bool, rightOp: ValueTask<bool>) =
103+
// simple
104+
if leftOp then ValueTask.True else rightOp
105+
106+
static member (?<-)(TaskOrOperator, leftOp: ValueTask<bool>, rightOp: bool) =
107+
if leftOp.IsCompletedSuccessfully then
108+
if leftOp.Result then
109+
ValueTask.True
110+
else
111+
ValueTask.fromResult rightOp
112+
else
113+
task {
114+
let! leftOperand = leftOp
115+
return leftOperand || rightOp
116+
}
117+
|> ValueTask.ofTask
118+
119+
static member (?<-)(TaskOrOperator, leftOp: bool, rightOp: #Task<bool>) = if leftOp then ValueTask.True else ValueTask.ofTask rightOp
120+
121+
static member (?<-)(TaskOrOperator, leftOp: #Task<bool>, rightOp: bool) =
122+
// while it may be more efficient to evaluate rh-side first, we should honor order of operations!
123+
if leftOp.IsCompletedSuccessfully then
124+
if leftOp.Result then
125+
ValueTask.True
126+
else
127+
ValueTask.fromResult rightOp
128+
else
129+
task {
130+
let! leftOperand = leftOp
131+
return leftOperand || rightOp
132+
}
133+
|> ValueTask.ofTask
134+
135+
static member (?<-)(TaskOrOperator, leftOp: ValueTask<_>, rightOp: ValueTask<_>) =
136+
if leftOp.IsCompletedSuccessfully then
137+
if leftOp.Result then ValueTask.True else rightOp
138+
else
139+
task {
140+
let! leftOperand = leftOp
141+
if leftOperand then return true else return! rightOp
142+
}
143+
|> ValueTask.ofTask
144+
145+
static member (?<-)(TaskOrOperator, leftOp: #Task<_>, rightOp: ValueTask<_>) =
146+
if leftOp.IsCompletedSuccessfully then
147+
if leftOp.Result then ValueTask.True else rightOp
148+
else
149+
task {
150+
let! leftOperand = leftOp
151+
if leftOperand then return true else return! rightOp
152+
}
153+
|> ValueTask.ofTask
154+
155+
static member (?<-)(TaskOrOperator, leftOp: ValueTask<_>, rightOp: #Task<_>) =
156+
if leftOp.IsCompletedSuccessfully then
157+
if leftOp.Result then
158+
ValueTask.True
159+
else
160+
ValueTask.ofTask rightOp
161+
else
162+
task {
163+
let! leftOperand = leftOp
164+
if leftOperand then return true else return! rightOp
165+
}
166+
|> ValueTask.ofTask
167+
168+
static member (?<-)(TaskOrOperator, leftOp: #Task<_>, rightOp: #Task<_>) =
169+
if leftOp.IsCompletedSuccessfully then
170+
if leftOp.Result then
171+
ValueTask.True
172+
else
173+
ValueTask.ofTask rightOp
174+
else
175+
task {
176+
let! leftOperand = leftOp
177+
if leftOperand then return true else return! rightOp
178+
}
179+
|> ValueTask.ofTask
180+
181+
[<AutoOpen>]
182+
module OperatorOverloads =
183+
184+
/// Binary 'and'. When used as a binary operator, the right-hand operand is evaluated only on demand.
185+
let inline (&&) leftOp rightOp : 'T = ((?<-) TaskAndOperator leftOp rightOp) // SCDU will get erased in release builds
186+
187+
/// Binary 'or'. When used as a binary operator, the right-hand operand is evaluated only on demand.
188+
let inline (||) leftOp rightOp : 'T = ((?<-) TaskOrOperator leftOp rightOp) // SCDU will get erased in release builds

0 commit comments

Comments
 (0)