Skip to content

Commit efe2171

Browse files
Merge pull request #24 from ENvironmentSet/add-fp-ts-monad-instance
Make `Effectful` to be monad
2 parents 37c953e + 1aab595 commit efe2171

File tree

4 files changed

+143
-1
lines changed

4 files changed

+143
-1
lines changed

docs/api/README.md

+1
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
- [`hyogwa/core`](./core.md)
44
- [`hyogwa/runners`](./runners.md)
5+
- [`hyogwa/monad`](./monad.md)
56
- [`hyogwa/assistants`](./assistants.md)
67
- [`hyogwa/async-task`](./async-task.md)
78
- [`hyogwa/state`](./state.md)

docs/api/monad.md

+35
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
# `hyogwa/monad`
2+
3+
Module implementing functions needed for `Effectful` to be monad. Utilize functions here to define monad instance of
4+
`Effectful` when using other functional programming libraries like `fp-ts` and `fun`.
5+
6+
> **⚠️ CAUTION**: Strictly speaking, `Effectful` is not a monad. Evaluating the same effectful computation multiple times
7+
> will produce unsound result. Have your code run(via `yield*`, `handle` or runners.) effectful computations only once per instance.
8+
9+
## `map<E, A, B>(f: (a: A) => B, computation: Effectful<E, A>): Effectful<E, B>`
10+
11+
Applies function to result of a computation.
12+
13+
## `map<A, B>(f: (a: A) => B): <E>(computation: Effectful<E, A>) => Effectful<E, B>`
14+
15+
Lifts given function to work with effectful computations instead of plain values
16+
17+
## `of<T>(value: T): Effectful<never, T>`
18+
19+
Makes computation of the given value
20+
21+
## `ap<E, A, B>(wrappedF: Effectful<E, (a: A) => B>, computation: Effectful<E, A>): Effectful<E, B>`
22+
23+
Applies function inside effectful computation to result of another computation.
24+
25+
## `ap<E1, A, B>(wrappedF: Effectful<E1, (a: A) => B>): <E2>(computation: Effectful<E2, A>) => Effectful<E1 | E2, B>`
26+
27+
Unwrap function inside effectful computation as function for effectful computations.
28+
29+
## `chain<E, A, B>(f: (a: A) => Effectful<E, B>, computation: Effectful<E, A>): Effectful<E, B>`
30+
31+
Applies effectful function to effectful computation.
32+
33+
## `chain<E1, A, B>(f: (a: A) => Effectful<E1, B>): <E2>(computation: Effectful<E2, A>) => Effectful<E1 | E2, B>`
34+
35+
Lifts effectful function to be function of effectful computations.

package.json

+2-1
Original file line numberDiff line numberDiff line change
@@ -12,7 +12,8 @@
1212
"./env": "./src/env.ts",
1313
"./state": "./src/state.ts",
1414
"./log": "./src/log.ts",
15-
"./async-task": "./src/async-task.ts"
15+
"./async-task": "./src/async-task.ts",
16+
"./monad": "./src/monad.ts"
1617
},
1718
"sideEffects": false,
1819
"scripts": {

src/monad.ts

+105
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,105 @@
1+
import { Effectful } from './core';
2+
3+
/**
4+
* Applies function to result of a computation
5+
*
6+
* @beta
7+
*
8+
* @param f - A function to apply
9+
* @param computation - A computation whose result will be applied
10+
* @returns A computation mapped by `f`
11+
*/
12+
export function map<E, A, B>(f: (a: A) => B, computation: Effectful<E, A>): Effectful<E, B>
13+
/**
14+
* Lifts given function to work with effectful computations instead of plain values
15+
*
16+
* @beta
17+
*
18+
* @param f - A function to lift
19+
* @returns lifted function
20+
*/
21+
export function map<A, B>(f: (a: A) => B): <E>(computation: Effectful<E, A>) => Effectful<E, B>
22+
export function map<E, A, B>(f: (a: A) => B, computation?: Effectful<E, A>)
23+
: Effectful<E, B> | (<E>(computation: Effectful<E, A>) => Effectful<E, B>) {
24+
if (!computation) return function* (computation) { return f(yield* computation) }
25+
else return (function* () { return f(yield* computation) })()
26+
}
27+
28+
/**
29+
* Makes computation of the given value
30+
*
31+
* @beta
32+
*
33+
* @param value
34+
* @returns pure computation only producing the given value
35+
*/
36+
export function* of<T>(value: T): Effectful<never, T> {
37+
return value
38+
}
39+
40+
/**
41+
* Applies function inside effectful computation to result of another computation
42+
*
43+
* @beta
44+
*
45+
* @param wrappedF - An effectful computation wrapping a function
46+
* @param computation - An effectful computation whose result will be applied
47+
*/
48+
export function ap<E, A, B>(wrappedF: Effectful<E, (a: A) => B>, computation: Effectful<E, A>): Effectful<E, B>
49+
/**
50+
* Unwrap function inside effectful computation as function for effectful computations
51+
*
52+
* @beta
53+
*
54+
* @param wrappedF - A function to unwrap
55+
* @returns an unwrapped function for effectful computations
56+
*/
57+
export function ap<E1, A, B>(wrappedF: Effectful<E1, (a: A) => B>)
58+
: <E2>(computation: Effectful<E2, A>) => Effectful<E1 | E2, B>
59+
export function ap<E, A, B>(wrappedF: Effectful<E, (a: A) => B>, computation?: Effectful<E, A>)
60+
: Effectful<E, B> | (<E2>(computation: Effectful<E2, A>) => Effectful<E | E2, B>) {
61+
if (!computation) return function* (computation) {
62+
const f = yield* wrappedF
63+
const a = yield* computation
64+
65+
return f(a)
66+
}
67+
else return (function* () {
68+
const f = yield* wrappedF
69+
const a = yield* computation
70+
71+
return f(a)
72+
})()
73+
}
74+
75+
/**
76+
* Applies effectful function to effectful computation
77+
*
78+
* @beta
79+
*
80+
* @param f - An effectful function to apply
81+
* @param computation - An effectful computation to be applied
82+
*/
83+
export function chain<E, A, B>(f: (a: A) => Effectful<E, B>, computation: Effectful<E, A>): Effectful<E, B>
84+
/**
85+
* Lifts effectful function to be function of effectful computations
86+
*
87+
* @beta
88+
*
89+
* @param f - An effectful function to lift
90+
*/
91+
export function chain<E1, A, B>(f: (a: A) => Effectful<E1, B>)
92+
: <E2>(computation: Effectful<E2, A>) => Effectful<E1 | E2, B>
93+
export function chain<E, A, B>(f: (a: A) => Effectful<E, B>, computation?: Effectful<E, A>)
94+
: Effectful<E, B> | (<E2>(computation: Effectful<E2, A>) => Effectful<E | E2, B>) {
95+
if (!computation) return function* (computation) {
96+
const a = yield* computation
97+
98+
return yield* f(a)
99+
}
100+
else return (function* () {
101+
const a = yield* computation
102+
103+
return yield* f(a)
104+
})()
105+
}

0 commit comments

Comments
 (0)