Skip to content

Commit f88dfef

Browse files
committed
fixing while loops for option, voption, validation
1 parent a2e3ef6 commit f88dfef

File tree

10 files changed

+130
-120
lines changed

10 files changed

+130
-120
lines changed

src/FsToolkit.ErrorHandling/Option.fs

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,16 @@ namespace FsToolkit.ErrorHandling
33
[<RequireQualifiedAccess>]
44
module Option =
55

6+
let inline bind ([<InlineIfLambdaAttribute>] f) x =
7+
match x with
8+
| Some v -> f v
9+
| None -> None
10+
11+
let inline map ([<InlineIfLambdaAttribute>] f) x =
12+
match x with
13+
| Some v -> Some(f v)
14+
| None -> None
15+
616
let inline ofValueOption (vopt: 'value voption) : 'value option =
717
match vopt with
818
| ValueSome v -> Some v

src/FsToolkit.ErrorHandling/OptionCE.fs

Lines changed: 15 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -17,12 +17,12 @@ module OptionCE =
1717
Option.bind binder input
1818

1919
// Could not get it to work solely with Source. In loop cases it would potentially match the #seq overload and ask for type annotation
20-
member inline this.Bind
21-
(
22-
m: 'input when 'input: null,
23-
[<InlineIfLambda>] binder: 'input -> 'output option
24-
) : 'output option =
25-
this.Bind(Option.ofObj m, binder)
20+
// member inline this.Bind
21+
// (
22+
// m: 'input when 'input: null,
23+
// [<InlineIfLambda>] binder: 'input -> 'output option
24+
// ) : 'output option =
25+
// this.Bind(Option.ofObj m, binder)
2626

2727
member inline this.Zero() : unit option = this.Return()
2828

@@ -71,21 +71,16 @@ module OptionCE =
7171
member inline this.While
7272
(
7373
[<InlineIfLambda>] guard: unit -> bool,
74-
[<InlineIfLambda>] computation: unit -> unit option
74+
[<InlineIfLambda>] generator: unit -> unit option
7575
) : unit option =
76-
if guard () then
77-
let mutable whileBuilder = Unchecked.defaultof<_>
78-
79-
whileBuilder <-
80-
fun () ->
81-
this.Bind(
82-
this.Run computation,
83-
(fun () -> if guard () then this.Run whileBuilder else this.Zero())
84-
)
85-
86-
this.Run whileBuilder
87-
else
88-
this.Zero()
76+
77+
let rec whileBuilder () =
78+
if guard () then
79+
this.Bind(this.Run(fun () -> generator ()), (fun () -> this.Run(fun () -> whileBuilder ())))
80+
else
81+
this.Zero()
82+
83+
this.Run(fun () -> whileBuilder ())
8984

9085
member inline this.For
9186
(

src/FsToolkit.ErrorHandling/ResultCE.fs

Lines changed: 6 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -75,12 +75,11 @@ module ResultCE =
7575
[<InlineIfLambda>] generator: unit -> Result<unit, 'error>
7676
) : Result<unit, 'error> =
7777

78-
let rec whileBuilder =
79-
fun () ->
80-
if guard () then
81-
this.Bind(this.Run(fun () -> generator ()), (fun () -> this.Run(fun () -> whileBuilder ())))
82-
else
83-
this.Zero()
78+
let rec whileBuilder () =
79+
if guard () then
80+
this.Bind(this.Run(fun () -> generator ()), (fun () -> this.Run(fun () -> whileBuilder ())))
81+
else
82+
this.Zero()
8483

8584
this.Run(fun () -> whileBuilder ())
8685

@@ -92,7 +91,7 @@ module ResultCE =
9291
) : Result<unit, 'TError> =
9392
this.Using(
9493
sequence.GetEnumerator(),
95-
fun enum -> this.While(enum.MoveNext, this.Delay(fun () -> binder enum.Current))
94+
fun enum -> this.While((fun () -> enum.MoveNext()), this.Delay(fun () -> binder enum.Current))
9695
)
9796

9897
member inline _.BindReturn

src/FsToolkit.ErrorHandling/ValidationCE.fs

Lines changed: 7 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -74,19 +74,13 @@ module ValidationCE =
7474
[<InlineIfLambda>] guard: unit -> bool,
7575
[<InlineIfLambda>] generator: unit -> Validation<unit, 'error>
7676
) : Validation<unit, 'error> =
77-
if guard () then
78-
let mutable whileBuilder = Unchecked.defaultof<_>
79-
80-
whileBuilder <-
81-
fun () ->
82-
this.Bind(
83-
this.Run generator,
84-
(fun () -> if guard () then this.Run whileBuilder else this.Zero())
85-
)
86-
87-
this.Run whileBuilder
88-
else
89-
this.Zero()
77+
let rec whileBuilder () =
78+
if guard () then
79+
this.Bind(this.Run(fun () -> generator ()), (fun () -> this.Run(fun () -> whileBuilder ())))
80+
else
81+
this.Zero()
82+
83+
this.Run(fun () -> whileBuilder ())
9084

9185
member inline this.For
9286
(

src/FsToolkit.ErrorHandling/ValueOption.fs

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,17 @@ namespace FsToolkit.ErrorHandling
44
[<RequireQualifiedAccess>]
55
module ValueOption =
66

7+
8+
let inline bind ([<InlineIfLambdaAttribute>] f) x =
9+
match x with
10+
| ValueSome v -> f v
11+
| ValueNone -> ValueNone
12+
13+
let inline map ([<InlineIfLambdaAttribute>] f) x =
14+
match x with
15+
| ValueSome v -> ValueSome(f v)
16+
| ValueNone -> ValueNone
17+
718
let inline ofOption (opt: 'value option) : 'value voption =
819
match opt with
920
| Some v -> ValueSome v

src/FsToolkit.ErrorHandling/ValueOptionCE.fs

Lines changed: 8 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -72,19 +72,14 @@ module ValueOptionCE =
7272
[<InlineIfLambda>] guard: unit -> bool,
7373
[<InlineIfLambda>] generator: unit -> _ voption
7474
) : _ voption =
75-
if guard () then
76-
let mutable whileBuilder = Unchecked.defaultof<_>
77-
78-
whileBuilder <-
79-
fun () ->
80-
this.Bind(
81-
this.Run generator,
82-
(fun () -> if guard () then this.Run whileBuilder else this.Zero())
83-
)
84-
85-
this.Run whileBuilder
86-
else
87-
this.Zero()
75+
76+
let rec whileBuilder () =
77+
if guard () then
78+
this.Bind(this.Run(fun () -> generator ()), (fun () -> this.Run(fun () -> whileBuilder ())))
79+
else
80+
this.Zero()
81+
82+
this.Run(fun () -> whileBuilder ())
8883

8984
member inline this.For
9085
(

tests/FsToolkit.ErrorHandling.Tests/OptionCE.fs

Lines changed: 20 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -126,19 +126,26 @@ let ceTests =
126126
}
127127

128128
Expect.equal actual (Some data) "Should be ok"
129-
testCase "While"
130-
<| fun () ->
131-
let data = 42
132-
let mutable index = 0
133-
134-
let actual = option {
135-
while index < 10 do
136-
index <- index + 1
137-
138-
return data
139-
}
140-
141-
Expect.equal actual (Some data) "Should be ok"
129+
yield! [
130+
let maxIndices = [10; 1000000]
131+
for maxIndex in maxIndices do
132+
testCase <| sprintf "While - %i" maxIndex
133+
<| fun () ->
134+
let data = 42
135+
let mutable index = 0
136+
137+
let actual = option {
138+
while index < maxIndex do
139+
index <- index + 1
140+
141+
return data
142+
}
143+
144+
Expect.equal index maxIndex "Index should reach maxIndex"
145+
Expect.equal actual (Some data) "Should be ok"
146+
]
147+
148+
142149
testCase "For in"
143150
<| fun () ->
144151
let data = 42

tests/FsToolkit.ErrorHandling.Tests/ResultCE.fs

Lines changed: 17 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -233,34 +233,23 @@ let ``ResultCE using Tests`` =
233233

234234
let ``ResultCE loop Tests`` =
235235
testList "ResultCE loop Tests" [
236-
testCase "while"
237-
<| fun () ->
238-
let data = 42
239-
let maxLoop = 10
240-
let mutable index = maxLoop
241-
242-
let actual = result {
243-
while index < 10 do
244-
index <- index + 1
245-
246-
return data
247-
}
248-
Expect.equal index maxLoop "index should match maxLoop"
249-
Expect.equal actual (Result.Ok data) "Should be ok"
250-
testCase "while long"
251-
<| fun () ->
252-
let data = 42
253-
let mutable index = 0
254-
let maxLoop = 1000000
255-
let actual = result {
256-
while index < maxLoop do
257-
index <- index + 1
258-
259-
return data
260-
}
261-
262-
Expect.equal index (maxLoop ) "index should match maxLoop"
263-
Expect.equal actual (Result.Ok data) "Should be ok"
236+
yield! [
237+
let maxIndices = [10; 10000; 1000000]
238+
for maxIndex in maxIndices do
239+
testCase <| sprintf "While - %i" maxIndex
240+
<| fun () ->
241+
let data = 42
242+
let mutable index = 0
243+
244+
let actual = result {
245+
while index < maxIndex do
246+
index <- index + 1
247+
248+
return data
249+
}
250+
Expect.equal index (maxIndex) "Index should reach maxIndex"
251+
Expect.equal actual (Ok data) "Should be ok"
252+
]
264253
testCase "for in"
265254
<| fun () ->
266255
let data = 42

tests/FsToolkit.ErrorHandling.Tests/ValidationCE.fs

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -277,19 +277,24 @@ let ``ValidationCE using Tests`` =
277277

278278
let ``ValidationCE loop Tests`` =
279279
testList "ValidationCE loop Tests" [
280-
testCase "while"
281-
<| fun () ->
282-
let data = 42
283-
let mutable index = 0
284-
285-
let actual = validation {
286-
while index < 10 do
287-
index <- index + 1
288-
289-
return data
290-
}
291-
292-
Expect.equal actual (Result.Ok data) "Should be ok"
280+
yield! [
281+
let maxIndices = [10; 1000000]
282+
for maxIndex in maxIndices do
283+
testCase <| sprintf "While - %i" maxIndex
284+
<| fun () ->
285+
let data = 42
286+
let mutable index = 0
287+
288+
let actual = validation {
289+
while index < maxIndex do
290+
index <- index + 1
291+
292+
return data
293+
}
294+
295+
Expect.equal index maxIndex "Index should reach maxIndex"
296+
Expect.equal actual (Ok data) "Should be ok"
297+
]
293298
testCase "for in"
294299
<| fun () ->
295300
let data = 42

tests/FsToolkit.ErrorHandling.Tests/ValueOptionCE.fs

Lines changed: 18 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -129,19 +129,24 @@ let ceTests =
129129
}
130130

131131
Expect.equal actual (ValueSome data) "Should be ok"
132-
testCase "While"
133-
<| fun () ->
134-
let data = 42
135-
let mutable index = 0
136-
137-
let actual = voption {
138-
while index < 10 do
139-
index <- index + 1
140-
141-
return data
142-
}
143-
144-
Expect.equal actual (ValueSome data) "Should be ok"
132+
yield! [
133+
let maxIndices = [10; 1000000]
134+
for maxIndex in maxIndices do
135+
testCase <| sprintf "While - %i" maxIndex
136+
<| fun () ->
137+
let data = 42
138+
let mutable index = 0
139+
140+
let actual = voption {
141+
while index < maxIndex do
142+
index <- index + 1
143+
144+
return data
145+
}
146+
147+
Expect.equal index maxIndex "Index should reach maxIndex"
148+
Expect.equal actual (ValueSome data) "Should be ok"
149+
]
145150
testCase "For in"
146151
<| fun () ->
147152
let data = 42

0 commit comments

Comments
 (0)