-
Notifications
You must be signed in to change notification settings - Fork 2
/
Copy pathindex.ts
161 lines (132 loc) · 4.19 KB
/
index.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
import { pipe } from 'fp-ts/function';
import { sequenceS } from 'fp-ts/Apply';
import * as RD from '../src/index';
const putStrLn = (msg: string) => console.log(msg);
interface User {
name: string;
}
const jane: User = { name: 'Jane' };
const mark: User = { name: 'Mark' };
/**
* **Patterm matching**
*
* Consume `RemoteData` value by handling all the potential cases.
*/
const showMessage = RD.match<Error, string, string>({
notAsked: () => 'notAsked',
loading: () => 'loading',
success: (msg) => 'success: ' + msg,
failure: ({ message }) => 'failure: ' + message,
});
pipe(RD.of('Some message'), showMessage, putStrLn);
/**
* **Functor**
*
* Functions that work on some regulars types (say `T`) can be "lifted" to work
* on `RemoteData<E, T>` by using Functor's `map`
*/
const getName = ({ name }: User) => name;
const getErrorMessage = ({ message }: Error) => message;
// User -> string
getName(jane);
// Error -> string
getErrorMessage(new Error('Something went wrong'));
/**
* **Functor**
*
* Functions that work on some regulars types (say `T`) can be "lifted" to work
* on `RemoteData<E, T>` by using Functor's `map`
*/
const fetchedUser = RD.success(jane);
// RemoteData<Error, User> => RemoteData<Error, string>
pipe(fetchedUser, RD.map(getName));
/**
* Note that `map` is only able to apply the mapping function when the
* `RemoteData` is on `Success` state, otherwise the transformation is ignored.
*
* This behaviour is maintained in the functions from the other typeclasses. And
* it is exactly what makes working with them great, one doesn't have to
* imperatively check on every step if the value is there or not, we can apply
* all the necessary transformations and combinations and then at the end do a
* declaraty `match` to handle all the potential cases.
*/
pipe(RD.notAsked, RD.map(getName));
pipe(RD.loading, RD.map(getName));
pipe(RD.failure(new Error('Some error')), RD.map(getName));
/**
* **Bifunctor**
*
* Since `RemoteData` takes to type arguments we can not only map on the
* success, as we saw already, but also on the failure (ie. "the value on the left").
*/
// RemoteData<string, User> -> RemoteData<string, User>
pipe(fetchedUser, RD.mapLeft(getErrorMessage));
// RemoteData<Error, User> -> RemoteData<string, string>
pipe(fetchedUser, RD.bimap(getErrorMessage, getName));
/**
* **Apply** / **Applicative**
*
* Sometimes applying a single argument function inside a `RemoteData` isn't
* enough. We might have functions with several arguments we want to apply. We
* can lift those as well by using `ap`.
*/
const loves = (a: User) => (b: User) => `${a.name} loves ${b.name}`;
pipe(loves(jane)(mark), (msg) => `Unlifted: ${msg}`, putStrLn);
// Apply one argument at a time
const apResult = pipe(
// RemoteData<never, (user: User) => (user: User) => string>
RD.of(loves),
// RemoteData<never, (user: User) => string>
RD.ap(RD.of(mark)),
// RemoteData<never, string>
RD.ap(RD.of(jane)),
);
pipe(
apResult,
RD.mapLeft(() => new Error('xD')),
showMessage,
putStrLn,
);
/**
* `ap` is quite cumbersome to use. The `Apply` module provides `sequenceS` and
* `sequenceT` (S as in struct, T as in tuple), they behave similarly to `Promise.all`.
*/
const sequenceRD = sequenceS(RD.Apply);
pipe(
// { a: RemoteData<E, A>, b: RemoteData<E, B> } => RemoteData<E, { a: A, b: B }>
//
// { jane: RemoteData<E, User>, mark: RemoteData<E, User> }
// => RemoteData<E, { jane: User, mark: User }>
sequenceRD({ jane: RD.of(jane), mark: RD.of(mark) }),
RD.map(({ jane, mark }) => loves(jane)(mark)),
showMessage,
putStrLn,
);
/**
* **Chain** / **Monad**
*
* Chain operations that return another `RemoteData`.
*
* When apply a function that goes from `A` to `RemoteData<E, B>`, using `map`
* would result in nested `RemoteData`s.
*
* `chain` chandles that for us.
*/
const exclaim = ({ name }: User): RD.RemoteData<Error, User> =>
RD.success({ name: name + '!' });
pipe(
fetchedUser,
// RemoteData<E, User> => RemoteData<E, RemoteData<E, User>>
RD.map(exclaim),
);
const chainResult = pipe(
fetchedUser,
// RemoteData<E, User> => RemoteData<E, User>
RD.chain(exclaim),
);
pipe(
chainResult,
RD.map((u) => u.name),
showMessage,
putStrLn,
);