@synple.dev/core 0.0.0 → 0.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -6,10 +6,14 @@ To install dependencies:
6
6
  bun install
7
7
  ```
8
8
 
9
- To run:
9
+ To build the application:
10
10
 
11
11
  ```bash
12
- bun run index.ts
12
+ bun build
13
13
  ```
14
14
 
15
- This project was created using `bun init` in bun v1.3.14. [Bun](https://bun.com) is a fast all-in-one JavaScript runtime.
15
+ To publish the application:
16
+
17
+ ```bash
18
+ bun publish --access public
19
+ ```
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@synple.dev/core",
3
- "version": "0.0.0",
3
+ "version": "0.2.0",
4
4
  "module": "index.ts",
5
5
  "type": "module",
6
6
  "private": false,
@@ -11,9 +11,11 @@
11
11
  "typescript": "^5"
12
12
  },
13
13
  "dependencies": {
14
- "bunup": "^0.16.32"
14
+ "bunup": "^0.16.32",
15
+ "vitest": "^4.1.8"
15
16
  },
16
17
  "scripts": {
17
- "build": "bunup"
18
+ "build": "bunup",
19
+ "test": "vitest"
18
20
  }
19
21
  }
@@ -0,0 +1,3 @@
1
+ ## Monads ?
2
+
3
+ Monads are a simple concept. TO sum it up with a bit of humour, I could say it's simply a monoid in the category of endofunctors, but without a strong knowledge of the basis of category theory, that would not be easy to understand. TO be more simple, monads are an object that allow you to separate the structure of an algorithm, from the result of each step of the algorithm. It gives you some ways to functionally handle the results and errors arising from computations.
@@ -0,0 +1,112 @@
1
+ export interface Side<T, Err> {
2
+
3
+ /**
4
+ * Returns the inner value of the monad, or a default value if it cannot be computed (in case of error).
5
+ * @param defaultValue the value to return if the computation cannot come to an end.
6
+ */
7
+ or(defaultValue: T): T
8
+
9
+ /**
10
+ * This flat map function takes a callback that can transform the current value in a value of another type
11
+ * wrapped in a monad, either with it being a success and a failure. It allows an easy chaining of methods
12
+ * after this call, chaining the different fmap calls until there is an error, or to the full extend of the
13
+ * computation.
14
+ *
15
+ * @param fct the function transforming the current value into a monad.
16
+ */
17
+ fmap<U>(fct: (item: T) => Side<U, Err>): Side<U, Err>
18
+
19
+ /**
20
+ * Applies a function to the inner value of the computation, eventually transforming it into a new type. On
21
+ * a fundamental level, the function passed here is just a morphism in the category of types, and after it
22
+ * has been applied to the current value, the result is raised anew in the world of monads to chain further
23
+ * computations.
24
+ *
25
+ * @param fct the function transforming the current value, eventually changing its type.
26
+ */
27
+ map<U>(fct: (item: T) => U): Side<U, Err>
28
+
29
+ /**
30
+ * Applies a function to the current value without waiting for its executation to end. It's only applied if
31
+ * the current value is a success. To apply a function like this on an error, use the tapError method. This
32
+ * method, and its sibling method tapError, are usually used to apply side-effect without waiting for them.
33
+ * A good example of that would be : logging the success of a computation.
34
+ *
35
+ * @param fct
36
+ */
37
+ tap(fct: (item: T) => void | Promise<void>): Side<T, Err>
38
+
39
+ /**
40
+ * The opposite of the tap function, applying a side-effect only if the current computation is a failure. It
41
+ * can be used, as tap is used, to log an error happening for example.
42
+ *
43
+ * @param fct the function generating the side-effect to execute.
44
+ */
45
+ tapError(fct: (error: Err) => void | Promise<void>): Side<T, Err>
46
+ }
47
+
48
+ class Success<T, Err> implements Side<T, Err> {
49
+ constructor(protected readonly item: T) { }
50
+
51
+ public or(_: T) {
52
+ return this.item
53
+ }
54
+
55
+ public fmap<U>(fct: (item: T) => Side<U, Err>): Side<U, Err> {
56
+ return fct(this.item)
57
+ }
58
+
59
+ public map<U>(fct: (item: T) => U): Side<U, Err> {
60
+ return some(fct(this.item))
61
+ }
62
+
63
+ public tap(fct: (item: T) => void | Promise<void>): Side<T, Err> {
64
+ fct(this.item)
65
+ return some(this.item)
66
+ }
67
+
68
+ public tapError(_: (error: Err) => void | Promise<void>): Side<T, Err> {
69
+ return some(this.item)
70
+ }
71
+ }
72
+
73
+ class Failure<T, Err> implements Side<T, Err> {
74
+ constructor(protected readonly error: Err) { }
75
+
76
+ public or(defaultValue: T) {
77
+ return defaultValue
78
+ }
79
+
80
+ public fmap<U>(_: (item: T) => Side<U, Err>): Side<U, Err> {
81
+ return fail(this.error)
82
+ }
83
+
84
+ public map<U>(_: (item: T) => U): Side<U, Err> {
85
+ return fail(this.error)
86
+ }
87
+
88
+ public tap(_: (item: T) => void | Promise<void>): Side<T, Err> {
89
+ return fail(this.error)
90
+ }
91
+
92
+ public tapError(fct: (error: Err) => void | Promise<void>): Side<T, Err> {
93
+ fct(this.error)
94
+ return fail(this.error)
95
+ }
96
+ }
97
+
98
+ /**
99
+ * Creates the first step of a computation, considered a success as nothing has already failed.
100
+ * @param item the initial value for the computation.
101
+ */
102
+ export function some<T, Err>(item: T): Success<T, Err> {
103
+ return new Success<T, Err>(item)
104
+ }
105
+
106
+ /**
107
+ * Creates a failing computation, often returned as result of a flat map in another monad.
108
+ * @param error the error returned by the computation.
109
+ */
110
+ export function fail<T, Err>(error: Err): Failure<T, Err> {
111
+ return new Failure<T, Err>(error)
112
+ }
@@ -0,0 +1,8 @@
1
+ import { describe, expect, it } from "vitest";
2
+ import { hello } from "../src";
3
+
4
+ describe('hello', () => {
5
+ it("Returns the correct message", () => {
6
+ expect(hello()).toEqual("Hello world!")
7
+ })
8
+ })
@@ -0,0 +1,70 @@
1
+ import { describe, it, expect, beforeEach } from "vitest"
2
+ import { fail, some } from "../../src/monads/either"
3
+
4
+ describe("some", () => {
5
+ it("Returns a wrapper containing the original value as a success monad", () => {
6
+ expect(some<number, number>(42).or(13)).toEqual(42)
7
+ })
8
+ })
9
+
10
+ describe("fail", () => {
11
+ it("Returns a wrapper containing an error monad returning a default value when interrogated", () => {
12
+ expect(fail<number, number>(42).or(13)).toEqual(13)
13
+ })
14
+ })
15
+
16
+ describe("fmap", () => {
17
+ const init = some<number, string>(42)
18
+
19
+ it("Returns a failure if the underlying function returns a failure", () => {
20
+ const fct = (n: number) => fail<number, string>("42")
21
+ expect(init.fmap(fct).or(13)).toEqual(13)
22
+ })
23
+ it("Returns a success if the underlying function returns a success", () => {
24
+ const fct = (n: number) => some<string, string>(`${n}`)
25
+ expect(init.fmap(fct).or("test")).toEqual("42")
26
+ })
27
+ })
28
+
29
+ describe("map", () => {
30
+ const stringify = (n: number) => `${n}`
31
+
32
+ it("Returns a failure if the underlying function returns a failure", () => {
33
+ expect(some<number, number>(42).map(stringify).or("13")).toEqual("42")
34
+ })
35
+ it("Returns a success if the underlying function returns a success", () => {
36
+ expect(fail<number, number>(42).map(stringify).or("13")).toEqual("13")
37
+ })
38
+ })
39
+
40
+ describe("Side effects dependant methods", () => {
41
+ // Used for the side effect built to pass to the tap method.
42
+ let num: number;
43
+
44
+ const affect = (n: number) => {
45
+ num = n;
46
+ }
47
+
48
+ beforeEach(() => affect(0))
49
+
50
+ describe("tap", () => {
51
+ it("Executes the side-effect when called on a success monad", () => {
52
+ some<number, number>(42).tap(affect)
53
+ expect(num).toEqual(42)
54
+ })
55
+ it("Does not execute the side-effect when called on a failure monad", () => {
56
+ fail<number, number>(42).tap(affect)
57
+ expect(num).toEqual(0)
58
+ })
59
+ })
60
+ describe("tapError", () => {
61
+ it("Does not execute the side-effect when called on a success monad", () => {
62
+ some<number, number>(42).tapError(affect)
63
+ expect(num).toEqual(0)
64
+ })
65
+ it("Executes the side-effect when called on a failure monad", () => {
66
+ fail<number, number>(42).tapError(affect)
67
+ expect(num).toEqual(42)
68
+ })
69
+ })
70
+ })
package/index.ts DELETED
@@ -1 +0,0 @@
1
- console.log("Hello via Bun!");