Skip to content

Commit 97933c5

Browse files
committed
Use assembly AutoOpen trick
1 parent c29a6f7 commit 97933c5

File tree

1 file changed

+43
-10
lines changed

1 file changed

+43
-10
lines changed

src/FsToolkit.ErrorHandling.IcedTasks/CancellableTaskValidationCE.fs

Lines changed: 43 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -533,7 +533,6 @@ module CancellableTaskValidationCE =
533533
BackgroundCancellableTaskValidationBuilder.RunDynamic(code)
534534

535535

536-
[<AutoOpen>]
537536
module CancellableTaskValidationBuilder =
538537

539538
let cancellableTaskValidation = CancellableTaskValidationBuilder()
@@ -542,7 +541,6 @@ module CancellableTaskValidationCE =
542541
BackgroundCancellableTaskValidationBuilder()
543542

544543

545-
[<AutoOpen>]
546544
module LowestPriority =
547545

548546
type CancellableTaskValidationBuilderBase with
@@ -628,7 +626,6 @@ module CancellableTaskValidationCE =
628626
.GetAwaiter()
629627

630628

631-
[<AutoOpen>]
632629
module LowerPriority =
633630

634631
type CancellableTaskValidationBuilderBase with
@@ -684,7 +681,6 @@ module CancellableTaskValidationCE =
684681
})
685682
.GetAwaiter()
686683

687-
[<AutoOpen>]
688684
module LowerPriority2 =
689685
// Low priority extensions
690686
type CancellableTaskValidationBuilderBase with
@@ -724,7 +720,6 @@ module CancellableTaskValidationCE =
724720

725721
fun (ct: CancellationToken) -> Awaitable.GetAwaiter(t)
726722

727-
[<AutoOpen>]
728723
module LowPriority =
729724
// Low priority extensions
730725
type CancellableTaskValidationBuilderBase with
@@ -761,7 +756,6 @@ module CancellableTaskValidationCE =
761756
)
762757
)
763758

764-
[<AutoOpen>]
765759
module MediumPriority =
766760
type Microsoft.FSharp.Control.Async with
767761

@@ -816,7 +810,6 @@ module CancellableTaskValidationCE =
816810
member inline _.Source([<InlineIfLambda>] t: CancellableTaskResult<'T, 'Error>) =
817811
CancellableTask.map Validation.ofResult t
818812

819-
[<AutoOpen>]
820813
module HighPriority =
821814

822815
type CancellableTaskValidationBuilder with
@@ -827,16 +820,15 @@ module CancellableTaskValidationCE =
827820
member inline _.Source([<InlineIfLambda>] t: CancellableTaskValidation<'T, 'Error>) =
828821
fun ct -> (t ct).GetAwaiter()
829822

830-
831823
member inline _.Source(t: Task<Validation<'T, 'Error>>) =
832824
fun (ct: CancellationToken) -> t.GetAwaiter()
833825

834-
835826
member inline _.Source(t: Async<Validation<'T, 'Error>>) =
836827
fun ct -> Async.StartAsTask(t, cancellationToken = ct).GetAwaiter()
837828

838-
[<AutoOpen>]
839829
module AsyncExtensions =
830+
open MediumPriority
831+
840832
type Microsoft.FSharp.Control.AsyncBuilder with
841833

842834
member inline this.Bind
@@ -1094,3 +1086,44 @@ module CancellableTaskValidationCE =
10941086
return Validation.zip r1 r2
10951087

10961088
}
1089+
1090+
// What's going on here?
1091+
//
1092+
// F# method overload resolution has some weird quirks we're taking advantage of to allow
1093+
// for binding (`let!/do!/return!`) many various types (Such as Task/Async/Result/Validation)
1094+
// in a computation expression. .The gist is, any member methods attached to the type itself
1095+
// (the Builder object) will be preferred above all else when selection overloads to resolve. It
1096+
// will then use the the most recent Extension Methods that have been opened. The way we structure
1097+
// these overloads is to provide the most "concrete" overloads first, and then the more generic
1098+
// ones later. For example, `Validation` is defined as a `Result<'T, 'Error list>`, but we also
1099+
// want to be able to bind to `Result` itself and create a list of errors from it. So we need to
1100+
// have a `Validation` member method in a higher module, and then a `Result` member method
1101+
// somewhere lower. Another example is `Task<Result<'T, 'Error>>` vs `Task<'T>`. We want to be able
1102+
// to bind to both, so we need to have a `Task<Result<'T, 'Error>>` member method in a higher
1103+
// module, and then a `Task<'T>` member method somewhere lower.
1104+
1105+
// NoEagerConstraintApplication also changes behavior of SRTP methods, read the
1106+
// TaskBuilder RFC for more info.
1107+
1108+
// The reason we do AutoOpens here instead of using the attribute on the module itself
1109+
// is because it may restrict how the implementation is relying on other sections, such as
1110+
// The MediumPriority module may use something from the HighPriority module. If we put the
1111+
// HighPriority module after the MediumPriority module it will fail to compile. So we don't want
1112+
// the order of the code itself to determine the priority, this allows us to control that ordering
1113+
// more explicitly.
1114+
//
1115+
// Additional readings:
1116+
// - [F# Computation Expression Method Overload Resolution Ordering](https://gist.github.com/TheAngryByrd/c8b9c8ebcda3bb162f425bfb281d2e2b)
1117+
// - [F# RFC FS-1097 - Task builder](https://github.com/fsharp/fslang-design/blob/main/FSharp-6.0/FS-1097-task-builder.md#feature-noeagerconstraintapplicationattribute)
1118+
// - ["Most concrete" tiebreaker for generic overloads](https://github.com/fsharp/fslang-suggestions/issues/905)
1119+
1120+
1121+
[<assembly: AutoOpen("FsToolkit.ErrorHandling.CancellableTaskValidationCE.LowestPriority")>]
1122+
[<assembly: AutoOpen("FsToolkit.ErrorHandling.CancellableTaskValidationCE.LowerPriority")>]
1123+
[<assembly: AutoOpen("FsToolkit.ErrorHandling.CancellableTaskValidationCE.LowerPriority2")>]
1124+
[<assembly: AutoOpen("FsToolkit.ErrorHandling.CancellableTaskValidationCE.LowPriority")>]
1125+
[<assembly: AutoOpen("FsToolkit.ErrorHandling.CancellableTaskValidationCE.MediumPriority")>]
1126+
[<assembly: AutoOpen("FsToolkit.ErrorHandling.CancellableTaskValidationCE.HighPriority")>]
1127+
[<assembly: AutoOpen("FsToolkit.ErrorHandling.CancellableTaskValidationCE.AsyncExtensions")>]
1128+
[<assembly: AutoOpen("FsToolkit.ErrorHandling.CancellableTaskValidationCE.CancellableTaskValidationBuilderModule")>]
1129+
do ()

0 commit comments

Comments
 (0)