Skip to content

Commit d83d74b

Browse files
committed
feat(3.0.0-rc.3): document and test effect.ts#mapEither
This change adds documentation and testint to mapEither in the effect.ts module. Additionally, a change was mad to effect.ts#swap to use either.ts#swap for the inner changes. Lastly, the ./scripts/coverage.sh script was moved to flake.nix, dependencies on darkhttpd and jq were removed, the coverage script now outputs only a summary to the cli (this may change), and the script can be run by calling `coverage`. I think in the future I'll move the script back to the ./scripts directory but source it in flake.nix. I'll seperate scripts for testing, doc testing, formatting, and such, and compose them properly, allowing the coverage directory to stay between runs but be ignored by git.
1 parent c5b84c6 commit d83d74b

File tree

6 files changed

+329
-150
lines changed

6 files changed

+329
-150
lines changed

deno.json

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "@baetheus/fun",
3-
"version": "3.0.0-rc.2",
3+
"version": "3.0.0-rc.3",
44
"exports": {
55
"./applicable": "./applicable.ts",
66
"./array": "./array.ts",

deno.lock

Lines changed: 90 additions & 93 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

effect.ts

Lines changed: 62 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -47,9 +47,8 @@ import { pipe } from "./fn.ts";
4747
*
4848
* @since 2.3.5
4949
*/
50-
export type Effect<D extends unknown[], B, A, C extends unknown[] = D> = (
51-
...d: D
52-
) => Promise<[Either<B, A>, ...C]>;
50+
export type Effect<D extends unknown[], B, A, C extends unknown[] = unknown[]> =
51+
(...d: D) => Promise<[Either<B, A>, ...C]>;
5352

5453
/**
5554
* Specifies Effect as a Higher Kinded Type with a fixed state type S,
@@ -234,7 +233,7 @@ export function tryCatch<S extends unknown[], B, A>(
234233
*/
235234
export function wrap<A, S extends unknown[] = unknown[]>(
236235
a: A,
237-
): Effect<S, never, A> {
236+
): Effect<S, never, A, S> {
238237
return (...s) => P.resolve([E.right(a), ...s]);
239238
}
240239

@@ -258,7 +257,7 @@ export function wrap<A, S extends unknown[] = unknown[]>(
258257
*/
259258
export function fail<B, S extends unknown[] = unknown[]>(
260259
b: B,
261-
): Effect<S, B, never> {
260+
): Effect<S, B, never, S> {
262261
return (...s) => P.resolve([E.left(b), ...s]);
263262
}
264263

@@ -280,7 +279,7 @@ export function fail<B, S extends unknown[] = unknown[]>(
280279
*/
281280
export function right<A, S extends unknown[] = unknown[]>(
282281
a: A,
283-
): Effect<S, never, A> {
282+
): Effect<S, never, A, S> {
284283
return wrap(a);
285284
}
286285

@@ -302,7 +301,7 @@ export function right<A, S extends unknown[] = unknown[]>(
302301
*/
303302
export function left<B, S extends unknown[] = unknown[]>(
304303
b: B,
305-
): Effect<S, B, never> {
304+
): Effect<S, B, never, S> {
306305
return fail(b);
307306
}
308307

@@ -336,10 +335,7 @@ export function left<B, S extends unknown[] = unknown[]>(
336335
export function swap<S extends unknown[], A, B, O extends unknown[]>(
337336
ua: Effect<S, B, A, O>,
338337
): Effect<S, A, B, O> {
339-
return async (...s) => {
340-
const [ea, ...o] = await ua.apply(ua, s);
341-
return [E.swap(ea), ...o];
342-
};
338+
return pipe(ua, mapEither(E.swap));
343339
}
344340

345341
/**
@@ -451,6 +447,60 @@ export function mapSecond<B, J>(
451447
};
452448
}
453449

450+
/**
451+
* Apply a function to transform the Either value of an Effect. Unlike `map`
452+
* and `mapSecond` which transform only success or error values respectively,
453+
* `mapEither` allows you to transform the entire Either, enabling operations
454+
* like swapping, conditional transformations, or mapping both sides at once.
455+
* The state is passed through unchanged.
456+
*
457+
* @example
458+
* ```ts
459+
* import * as Eff from "./effect.ts";
460+
* import * as E from "./either.ts";
461+
* import { pipe } from "./fn.ts";
462+
*
463+
* // Transform success values
464+
* const transformSuccess = (ea: E.Either<string, number>) =>
465+
* E.isRight(ea) ? E.right(`Success: ${ea.right}`) : ea;
466+
*
467+
* const effect1 = pipe(
468+
* Eff.right(42),
469+
* Eff.mapEither(transformSuccess)
470+
* );
471+
*
472+
* const result1 = await effect1("state");
473+
* // [E.right("Success: 42"), "state"]
474+
*
475+
* // Swap success and error
476+
* const swapEither = (ea: E.Either<string, number>) =>
477+
* E.isRight(ea) ? E.left(`Error: ${ea.right}`) : E.right(`Success: ${ea.left}`);
478+
*
479+
* const effect2 = pipe(
480+
* Eff.right(42),
481+
* Eff.mapEither(swapEither)
482+
* );
483+
*
484+
* const result2 = await effect2("state");
485+
* // [E.left("Error: 42"), "state"]
486+
*
487+
* // Transform error codes to messages
488+
* const handleErrors = (ea: E.Either<number, string>) =>
489+
* E.isRight(ea) ? ea : E.left(
490+
* ea.left === 404 ? "Not Found" : `Error ${ea.left}`
491+
* );
492+
*
493+
* const effect3 = pipe(
494+
* Eff.left(404),
495+
* Eff.mapEither(handleErrors)
496+
* );
497+
*
498+
* const result3 = await effect3("state");
499+
* // [E.left("Not Found"), "state"]
500+
* ```
501+
*
502+
* @since 3.0.0-rc.2
503+
*/
454504
export function mapEither<B, A, I, J>(
455505
fee: (ea: Either<B, A>) => Either<J, I>,
456506
): <S extends unknown[], O extends unknown[]>(
@@ -461,6 +511,7 @@ export function mapEither<B, A, I, J>(
461511
return [fee(ea), ...o];
462512
};
463513
}
514+
464515
/**
465516
* Apply a function wrapped in an Effect to a value wrapped in an Effect.
466517
* Both Effects must succeed for the application to succeed. The function

flake.nix

Lines changed: 39 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -7,33 +7,44 @@
77
};
88

99
outputs = { self, nixpkgs, flake-utils }:
10-
flake-utils.lib.eachDefaultSystem (system:
11-
let
12-
pkgs = import nixpkgs { inherit system; };
13-
14-
fun = with pkgs; derivation {
15-
inherit system;
16-
name = "fun";
17-
src = ./.;
18-
cp = "${coreutils}/bin/cp";
19-
mkdir = "${coreutils}/bin/mkdir";
20-
builder = "${dash}/bin/dash";
21-
args = [ "-c" "$mkdir $out; $cp $src/*.ts $out;" ];
22-
};
23-
24-
shell = with pkgs; mkShell {
25-
buildInputs = [ deno jq lcov ];
26-
};
27-
28-
in
29-
{
30-
# Packages
31-
packages.fun = fun;
32-
packages.default = fun;
33-
34-
# Shells
35-
devShell = shell;
36-
}
37-
);
10+
flake-utils.lib.eachDefaultSystem (system: let
11+
pkgs = import nixpkgs { inherit system; };
12+
mkScript = pkgs.writeShellScriptBin;
13+
14+
shell = with pkgs; mkShell {
15+
packages = [
16+
# Insert packages here
17+
deno
18+
19+
# Insert shell aliases here
20+
(mkScript "coverage" ''
21+
#!/usr/bin/env sh
22+
23+
if [ -z "$\{COVERAGE_DIR}" ]; then
24+
export COVERAGE_DIR="coverage"
25+
fi
26+
27+
exiting() {
28+
echo "Ctrl-C trapped, clearing coverage";
29+
rm -rf ./$COVERAGE_DIR;
30+
exit 0;
31+
}
32+
trap exiting SIGINT
33+
34+
rm -rf $COVERAGE_DIR
35+
deno fmt
36+
deno test --doc --parallel --trace-leaks --coverage=$COVERAGE_DIR
37+
deno coverage $COVERAGE_DIR
38+
rm -rf ./$COVERAGE_DIR
39+
'')
40+
];
41+
42+
shellHook = ''
43+
export COVERAGE_DIR="coverage"
44+
'';
45+
};
46+
in {
47+
devShells.default = shell;
48+
});
3849
}
3950

0 commit comments

Comments
 (0)