@@ -7,10 +7,11 @@ open BenchmarkDotNet.Attributes
77
88let okF x = x + 2
99let errorF x = x - 4
10-
1110let add x y = x + y
1211
1312module Result =
13+
14+ // let inline (|>>) ([<InlineIfLambda>] v : _ -> _) ([<InlineIfLambda>] f : _ -> _) = f v
1415 module Normal =
1516
1617 let either okF errorF x =
@@ -38,13 +39,9 @@ module Result =
3839 let eitherMap ( okF ) ( errorF ) x =
3940 either ( fun x -> x |> okF |> Result.Ok) ( fun y -> y |> errorF |> Result.Error) x
4041
41- let bind ( f ) x =
42- match x with
43- | Ok x -> f x
44- | Error e -> Error e
4542 let apply ( f ) x =
46- bind ( fun f' ->
47- bind ( fun x -> f' x |> Ok) x) f
43+ Result. bind ( fun f' ->
44+ Result. bind ( fun x -> f' x |> Ok) x) f
4845 let map2 ( f ) x y =
4946 ( apply ( apply ( Ok f) x) y)
5047
@@ -174,7 +171,23 @@ module Result =
174171 | Error e, _ -> Error e
175172 | _ , Error e -> Error e
176173
177- let inline map2 ( [<InlineIfLambda>] f ) x y =
174+ let inline map ( [<InlineIfLambda>] mapper : 'ok -> 'ok2 ) value =
175+ match value with
176+ | Ok x -> Ok( mapper x)
177+ | Error e -> Error e
178+
179+
180+ let inline bindSame ( [<InlineIfLambda>] binder : 'ok -> Result < 'ok , 'err >) value =
181+ match value with
182+ | Ok x -> binder x
183+ | Error _ -> value
184+
185+ let inline bind ( [<InlineIfLambda>] binder : 'ok -> Result < 'ok2 , 'err >) value =
186+ match value with
187+ | Ok x -> binder x
188+ | Error e -> Error e
189+
190+ let inline map2 ( [<InlineIfLambda>] f : 'a -> 'b -> 'c ) x y =
178191 match x, y with
179192 | Ok x, Ok y -> f x y |> Ok
180193 | Error e as z, _ -> Error e
@@ -191,7 +204,7 @@ type EitherMapBenchmarks () =
191204 member this.Result_Normal_NoComposition_EitherMap () =
192205 Ok 4
193206 |> Result.Normal.NoComposition.eitherMap okF errorF
194-
207+
195208 [<Benchmark>]
196209 member this.Result_Inlined_EitherMap () =
197210 Ok 4
@@ -224,6 +237,133 @@ type EitherMapBenchmarks () =
224237 |> Result.Alt.InlinedLambda.eitherMap okF errorF
225238
226239
240+ type ResultBuilder () =
241+ member __.Return ( value : 'T ) : Result < 'T , 'TError > = Ok value
242+
243+ // member inline __.ReturnFrom(result: Result<'T, 'TError>) : Result<'T, 'TError> = result
244+
245+ member this.Zero () : Result < unit , 'TError > = this.Return()
246+
247+ member __.Bind ( result : Result < 'T , 'TError >, binder : 'T -> Result < 'U , 'TError >) : Result < 'U , 'TError > =
248+ Result.bind binder result
249+
250+
251+ type ResultBuilderInlined () =
252+ member inline __.Return ( value : 'T ) : Result < 'T , 'TError > = Ok value
253+
254+ // member inline __.ReturnFrom(result: Result<'T, 'TError>) : Result<'T, 'TError> = result
255+
256+ member inline this.Zero () : Result < unit , 'TError > = this.Return()
257+
258+ member inline __.Bind ( result : Result < 'T , 'TError >, binder : 'T -> Result < 'U , 'TError >) : Result < 'U , 'TError > =
259+ Result.Inlined.bind binder result
260+
261+ type ResultBuilderInlinedLambda () =
262+ member inline __.Return ( value : 'T ) : Result < 'T , 'TError > = Ok value
263+
264+ member inline this.Zero () : Result < unit , 'TError > = this.Return()
265+
266+ member inline __.Bind ( result : Result < 'T , 'TError >, [<InlineIfLambda>] binder : 'T -> Result < 'T , 'TError >) : Result < 'T , 'TError > =
267+ Result.Alt.InlinedLambda.bindSame binder result
268+
269+ [<AutoOpen>]
270+ module ResultCEExtensions =
271+ type ResultBuilderInlinedLambda with
272+ member inline __.Bind ( result : Result < 'T , 'TError >, [<InlineIfLambda>] binder : 'T -> Result < 'U , 'TError >) : Result < 'U , 'TError > =
273+ Result.Alt.InlinedLambda.bind binder result
274+
275+ let result = ResultBuilder()
276+ let resultInlined = ResultBuilderInlined()
277+ let resultInlinedLambda = ResultBuilderInlinedLambda()
278+ [<MemoryDiagnoser>]
279+ type MapBenchmarks () =
280+ [<Benchmark( Baseline = true ) >]
281+ member this.Result_Normal_Map () =
282+ Result.map ( fun x -> x + 2 ) ( Ok 1 ) : Result<_, int>
283+
284+ [<Benchmark>]
285+ member this.Result_Alt_InlinedLambda_Map () =
286+ Result.Alt.InlinedLambda.map ( fun x -> x + 2 ) ( Ok 1 ) : Result<_, int>
287+
288+ let runTimes x action =
289+ let results = ResizeArray<_>()
290+ for i= 1 to x do
291+ action() |> results.Add
292+ results
293+
294+ [<MemoryDiagnoser>]
295+ type BindSameBenchmarks () =
296+ [<Benchmark( Baseline = true ) >]
297+ member this.Result_Alt_InlinedLambda_Bind () : ResizeArray < Result < int , string >> =
298+ runTimes 1000 ( fun () -> Result.Alt.InlinedLambda.bind ( fun x -> Ok( x + 2 ) ) ( Ok 1 ) )
299+ [<Benchmark>]
300+ member this.Result_Alt_InlinedLambda_BindSame () : ResizeArray < Result < int , string >> =
301+ runTimes 1000 ( fun () -> Result.Alt.InlinedLambda.bindSame ( fun x -> Ok( x + 2 ) ) ( Ok 1 ) )
302+
303+ [<Benchmark>]
304+ member this.Result_Alt_InlinedLambda_Bind_Error () : ResizeArray < Result < int , string >> =
305+ runTimes 1000 ( fun () -> Result.Alt.InlinedLambda.bind ( fun x -> Ok( x + 2 ) ) ( Error " no" ))
306+ [<Benchmark>]
307+ member this.Result_Alt_InlinedLambda_BindSame_Error () : ResizeArray < Result < int , string >> =
308+ runTimes 1000 ( fun () -> Result.Alt.InlinedLambda.bindSame ( fun x -> Ok( x + 2 ) ) ( Error " no" ))
309+
310+ [<MemoryDiagnoser>]
311+ type BindBenchmarks () =
312+ [<Benchmark( Baseline = true ) >]
313+ member this.Result_Normal_Bind () =
314+ Result.bind ( fun x -> Ok( x + 2 ) ) ( Ok 1 ) : Result< int, int>
315+
316+ [<Benchmark>]
317+ member this.Result_Alt_InlinedLambda_Bind () =
318+ Result.Alt.InlinedLambda.bind ( fun x -> Ok( x + 2 ) ) ( Ok 1 ) : Result< int, int>
319+
320+ let divide x y =
321+ match y with
322+ | 0 -> Error " Cannot divide by 0"
323+ | _ -> Ok ( x/ y)
324+
325+ [<MemoryDiagnoser>]
326+ type BindCEBenchmarks () =
327+ [<Benchmark( Baseline = true ) >]
328+ member this.Result_Normal_Bind_CE () =
329+ let action () = result {
330+ let! a = Ok 1
331+ let! b = Ok 3
332+ let! c = divide a b
333+ return c
334+ }
335+ action ()
336+
337+ [<Benchmark>]
338+ member this.Result_Alt_Inlined_Bind_CE () =
339+ let action () = resultInlined {
340+ let! a = Ok 1
341+ let! b = Ok 3
342+ let! c = divide a b
343+ return c
344+ }
345+ action ()
346+ [<Benchmark>]
347+ member this.Result_Alt_InlinedLambda_Bind_CE () =
348+ let action () = resultInlinedLambda {
349+ let! a = Ok 1
350+ let! b = Ok 3
351+ let! c = divide a b
352+ return c
353+ }
354+ action ()
355+
356+ [<Benchmark>]
357+ member this.Result_Alt_InlinedLambda_Bind_CE2 () : Result < int , string > =
358+ let action () = resultInlinedLambda {
359+ let! a = Ok 1
360+ let! b = Ok 3.0
361+ let! c = divide a ( int b)
362+ return c
363+ }
364+ action ()
365+
366+
227367[<MemoryDiagnoser>]
228368type Map2Benchmarks () =
229369
@@ -250,17 +390,11 @@ type Map2Benchmarks () =
250390
251391 [<Benchmark>]
252392 member this.Result_Alt_Map2 () =
253-
254393 Result.Alt.map2 add ( Ok 1 ) ( Ok 2 ) : Result< int, int>
255394 [<Benchmark>]
256395 member this.Result_Alt_Inlined_Map2 () =
257-
258396 Result.Alt.Inlined.map2 add ( Ok 1 ) ( Ok 2 ) : Result< int, int>
259-
260397 [<Benchmark>]
261398 member this.Result_Alt_InlinedLambda_Map2 () =
262399 Result.Alt.InlinedLambda.map2 add ( Ok 1 ) ( Ok 2 ) : Result< int, int>
263-
264- [<Benchmark>]
265- member this.Result_Alt_InlinedLambda2_Map2 () =
266- Result.Alt.InlinedLambda.map2 ( fun x y -> x + y) ( Ok 1 ) ( Ok 2 ) : Result< int, int>
400+
0 commit comments