Skip to content

Commit 49d3dd1

Browse files
committed
Add alt for AsyncResult
1 parent 70aa2df commit 49d3dd1

File tree

2 files changed

+129
-0
lines changed

2 files changed

+129
-0
lines changed

__tests__/Relude_AsyncResult_test.re

+92
Original file line numberDiff line numberDiff line change
@@ -1090,6 +1090,98 @@ describe("AsyncResult", () => {
10901090
|> toEqual(13)
10911091
);
10921092

1093+
test("alt (Init, Init)", () =>
1094+
AsyncResult.(alt(Init, Init) |> expect |> toEqual(init))
1095+
);
1096+
1097+
test("alt (Init, Loading)", () =>
1098+
AsyncResult.(alt(Init, Loading) |> expect |> toEqual(loading))
1099+
);
1100+
1101+
test("alt (Init, Reloading(Error))", () =>
1102+
AsyncResult.(alt(Init, Reloading(Error(0))) |> expect |> toEqual(init))
1103+
);
1104+
1105+
test("alt (Init, Reloading(Ok))", () =>
1106+
AsyncResult.(
1107+
alt(Init, Reloading(Ok(0))) |> expect |> toEqual(reloadingOk(0))
1108+
)
1109+
);
1110+
1111+
test("alt (Init, Complete(Error))", () =>
1112+
AsyncResult.(alt(Init, Complete(Error(0))) |> expect |> toEqual(init))
1113+
);
1114+
1115+
test("alt (Init, Complete(Ok))", () =>
1116+
AsyncResult.(
1117+
alt(Init, Complete(Ok(0))) |> expect |> toEqual(completeOk(0))
1118+
)
1119+
);
1120+
1121+
test("alt (Loading, Init)", () =>
1122+
AsyncResult.(alt(Loading, Init) |> expect |> toEqual(loading))
1123+
);
1124+
1125+
test("alt (Reloading(Error), Init)", () =>
1126+
AsyncResult.(alt(Reloading(Error(1)), Init) |> expect |> toEqual(init))
1127+
);
1128+
1129+
test("alt (Reloading(Error), Loading)", () =>
1130+
AsyncResult.(
1131+
alt(Reloading(Error(1)), Loading) |> expect |> toEqual(loading)
1132+
)
1133+
);
1134+
1135+
test("alt (Reloading(Error), Reloading(Error))", () =>
1136+
AsyncResult.(
1137+
alt(Reloading(Error(1)), Reloading(Error(2)))
1138+
|> expect
1139+
|> toEqual(reloadingError(1))
1140+
)
1141+
);
1142+
1143+
test("alt (Reloading(Error), Complete(Error))", () =>
1144+
AsyncResult.(
1145+
alt(Reloading(Error(1)), Complete(Error(2)))
1146+
|> expect
1147+
|> toEqual(completeError(2))
1148+
)
1149+
);
1150+
1151+
test("alt (Complete(Error), Init)", () =>
1152+
AsyncResult.(alt(Complete(Error(1)), Init) |> expect |> toEqual(init))
1153+
);
1154+
1155+
test("alt (Complete(Error), Loading)", () =>
1156+
AsyncResult.(
1157+
alt(Complete(Error(1)), Loading) |> expect |> toEqual(loading)
1158+
)
1159+
);
1160+
1161+
test("alt (Complete(Error), Reloading(Error))", () =>
1162+
AsyncResult.(
1163+
alt(Complete(Error(1)), Reloading(Error(2)))
1164+
|> expect
1165+
|> toEqual(completeError(1))
1166+
)
1167+
);
1168+
1169+
test("alt (Complete(Error), Reloading(Ok))", () =>
1170+
AsyncResult.(
1171+
alt(Complete(Error(1)), Reloading(Ok(2)))
1172+
|> expect
1173+
|> toEqual(reloadingOk(2))
1174+
)
1175+
);
1176+
1177+
test("alt (Complete(Error), Complete(Error))", () =>
1178+
AsyncResult.(
1179+
alt(Complete(Error(1)), Complete(Error(2)))
1180+
|> expect
1181+
|> toEqual(completeError(1))
1182+
)
1183+
);
1184+
10931185
test("fromAsyncData Init", () =>
10941186
AsyncData.init
10951187
|> AsyncResult.fromAsyncData

src/Relude_AsyncResult.re

+37
Original file line numberDiff line numberDiff line change
@@ -570,6 +570,36 @@ let toAsyncData: 'a. t('a, 'a) => Relude_AsyncData.t('a) =
570570
| Complete(Error(a)) => Complete(a)
571571
| Complete(Ok(a)) => Complete(a);
572572

573+
/**
574+
[AsyncResult.alt] compares two AsyncResult values and returns the "most
575+
successful" of the two. In practice, this means that [Complete] is preferred
576+
over all else, [Reloading] is preferred over [Loading], and [Loading] is
577+
preferred over [Init]. The exception to this rule is the case where one of the
578+
provided AsyncResult values has an inner [Result] in an error state, in which
579+
case the other value is always preferred.
580+
*/
581+
let alt: 'a 'e. (t('a, 'e), t('a, 'e)) => t('a, 'e) =
582+
(fa, fb) =>
583+
switch (fa, fb) {
584+
// when both are errors, prefer "complete" over "reloading"
585+
| (Reloading(Error(_)), Complete(Error(_)) as b) => b
586+
| (Complete(Error(_)) as a, Reloading(Error(_))) => a
587+
588+
// if both are the same error type, prefer the first
589+
| (Reloading(Error(_)) as a, Reloading(Error(_))) => a
590+
| (Complete(Error(_)) as a, Complete(Error(_))) => a
591+
592+
// prefer any non-error over an error
593+
| (Reloading(Error(_)), b)
594+
| (Complete(Error(_)), b) => b
595+
596+
| (a, Reloading(Error(_)))
597+
| (a, Complete(Error(_))) => a
598+
599+
// otherwise, no errors, fall back to AsyncData.alt
600+
| (a, b) => Relude_AsyncData.alt(a, b)
601+
};
602+
573603
/**
574604
Indicates if two AsyncResult values are in the same state, and have the same contained value.
575605
*/
@@ -614,8 +644,15 @@ module WithError = (E: TYPE) => {
614644
let bind = Monad.flat_map;
615645
include Relude_Extensions_Monad.MonadExtensions(Monad);
616646

647+
module Alt: ALT with type t('a) = t('a, E.t) = {
648+
include Functor;
649+
let alt = alt;
650+
};
651+
include Relude_Extensions_Alt.AltExtensions(Alt);
652+
617653
module Infix = {
618654
include Relude_Extensions_Functor.FunctorInfix(Functor);
655+
include Relude_Extensions_Alt.AltInfix(Alt);
619656
include Relude_Extensions_Apply.ApplyInfix(Apply);
620657
include Relude_Extensions_Monad.MonadInfix(Monad);
621658
};

0 commit comments

Comments
 (0)