1
1
# Functional
2
2
3
- [ ![ deno doc] ( https://doc.deno.land/badge.svg )] ( https://doc.deno.land/https/deno.land/x/functional/SumType.js )
3
+ Common Functional Programming Algebraic data types for JavaScript that is compatible with most modern browsers and Deno.
4
+
4
5
[ ![ deno land] ( http://img.shields.io/badge/available%20on-deno.land/x-lightgrey.svg?logo=deno&labelColor=black )] ( https://deno.land/x/cli_badges )
5
6
[ ![ deno version] ( https://img.shields.io/badge/deno-^1.3.2-lightgrey?logo=deno )] ( https://github.com/denoland/deno )
6
7
[ ![ GitHub release] ( https://img.shields.io/github/release/sebastienfilion/functional.svg )] ( https://github.com/sebastienfilion/functional/releases )
9
10
* [ Maybe] ( #maybe-type )
10
11
* [ Either] ( #either-type )
11
12
* [ IO] ( #io-type )
13
+ * [ Task] ( #task-type )
12
14
* [ TypeScript] ( #typescript )
15
+
16
+ # Usage
17
+
18
+ This example uses the Ramda library - for simplification - but you should be able to use any library that implements
19
+ the [ Fantasy-land specifications] ( https://github.com/fantasyland/fantasy-land ) .
20
+
21
+ ``` js
22
+ import {
compose ,
converge ,
lift ,
map ,
prop }
from " https://x.nest.land/[email protected] /source/index.js" ;
23
+ import Either from " https://deno.land/x/[email protected] /Either.js"
24
+ import Task from " https://deno.land/x/[email protected] /Task.js"
25
+
26
+ const fetchUser = userID => Task .wrap (_ => fetch (` ${ URL } /users/${ userID} ` ).then (response => response .json ()));
27
+
28
+ const sayHello = compose (
29
+ converge (
30
+ lift ((username , email ) => ` Hello ${ username} (${ email} )!` ),
31
+ [
32
+ map (prop (" username" )),
33
+ map (prop (" email" ))
34
+ ]
35
+ ),
36
+ fetchUser
37
+ );
38
+
39
+ // Calling `sayHello` results in an instance of `Task` keeping the function pure.
40
+ assert (Task .is (sayHello (userID)));
41
+
42
+ // Finally, calling `Task#run` will call `fetch` and return a promise
43
+ sayHello (userID).run ()
44
+ .then (container => {
45
+ // The returned value should be an instance of `Either.Right` or `Either.Left`
46
+ assert (Either .Right .is (container));
47
+ // Forcing to coerce the container to string will show that the final value is our message.
48
+ assert (
container .
toString (),
` Either.Right("Hello johndoe ([email protected] )!")` );
49
+ });
50
+
51
+ // sayHello(userID).run() === Either.Right("Hello johndoe ([email protected] )!")
52
+ ```
13
53
14
54
## Type factory
15
55
16
56
The Type factory can be used to build complex data structure.
17
57
18
58
``` js
19
- import { factorizeType } from " https://deno.land/x/functional/SumType.js"
59
+ import { factorizeType } from " https://deno.land/x/functional@v0.5.0 /SumType.js"
20
60
21
61
const Coordinates = factorizeType (" Coordinates" , [ " x" , " y" ]);
22
62
const vector = Coordinates (150 , 200 );
@@ -80,7 +120,7 @@ vector.toString();
80
120
## Type Sum factory
81
121
82
122
``` js
83
- import { factorizeSumType } from " https://deno.land/x/functional/SumType.js"
123
+ import { factorizeSumType } from " https://deno.land/x/functional@v0.5.0 /SumType.js"
84
124
85
125
const Shape = factorizeSumType (
86
126
" Shape" ,
@@ -162,7 +202,7 @@ oval.toString();
162
202
### Example of writing a binary tree with Sum Types
163
203
164
204
``` js
165
- import { factorizeSumType } from " https://deno.land/x/functional/SumType.js"
205
+ import { factorizeSumType } from " https://deno.land/x/functional@v0.5.0 /SumType.js"
166
206
167
207
const BinaryTree = factorizeSumType (' BinaryTree' , {
168
208
Node : [' left' , ' x' , ' right' ],
@@ -215,7 +255,7 @@ const tree =
215
255
The ` Maybe ` type represents potentially ` Just ` a value or ` Nothing ` .
216
256
217
257
``` js
218
- import Maybe from " https://deno.land/x/functional/Maybe.js"
258
+ import Maybe from " https://deno.land/x/functional@v0.5.0 /Maybe.js"
219
259
220
260
const container = Maybe .Just (42 );
221
261
@@ -240,7 +280,7 @@ This implementation of Maybe is a valid [`Filterable`](https://github.com/fantas
240
280
The ` Either ` type represents the possibility of two values; either an ` a ` or a ` b ` .
241
281
242
282
``` js
243
- import Either from " https://deno.land/x/functional/Either.js"
283
+ import Either from " https://deno.land/x/functional@v0.5.0 /Either.js"
244
284
245
285
const container = Either .Right (42 );
246
286
@@ -263,7 +303,7 @@ This implementation of Maybe is a valid [`Functor`](https://github.com/fantasyla
263
303
The ` IO ` type represents a function that access IO. It will be lazily executed when the ` #run ` method is called.
264
304
265
305
``` js
266
- import IO from " https://deno.land/x/functional/IO.js"
306
+ import IO from " https://deno.land/x/functional@v0.5.0 /IO.js"
267
307
268
308
// Eventually 42
269
309
const container = IO (_ => Promise .resolve (42 ));
@@ -285,14 +325,73 @@ This implementation of IO is a valid [`Functor`](https://github.com/fantasyland/
285
325
[ ` Applicative ` ] ( https://github.com/fantasyland/fantasy-land#applicative ) and
286
326
[ ` Monad ` ] ( https://github.com/fantasyland/fantasy-land#monad ) .
287
327
328
+ ## ` Task ` type
329
+
330
+ The ` Task ` type represents a function that access IO. It will be lazily executed when the ` #run ` method is called.
331
+ Unlike IO, the Task type also abstract away the promise making for a more intuitive experience.
332
+ Note that the function must return an instance of [ ` Either ` ] ( #either-type ) ; ` Either.Right ` to represent a success and
333
+ ` Either.Left ` to represent a failure. Also check-out the [ ` Task.wrap ` ] ( #task-wrap ) method.
334
+
335
+ If the runtime throws an error, the final value will be ` Either.Left(error) ` .
336
+
337
+ ``` js
338
+ import Either from " https://deno.land/x/[email protected] /Either.js" ;
339
+ import Task from " https://deno.land/x/[email protected] /Task.js"
340
+
341
+ // Eventually 42
342
+ const container = Task (_ => Promise .resolve (Either .Right (42 )));
343
+
344
+ const multiply = container .map (x => x * x);
345
+ const add = container .map (x => x + x);
346
+
347
+ // multiply === Task(Function)
348
+ // add === Task(Function)
349
+
350
+ const multiplyThenAdd = multiply .map (x => x + x);
351
+
352
+ // await multiply.run() === Either.Right(1764)
353
+ // await add.run() === Either.Right(84)
354
+ // await multiplyThenAdd.run() === Either.Right(3528)
355
+ ```
356
+
357
+ ### ` Task.wrap `
358
+
359
+ Create a wrapped instance of Task. An instance of ` Task ` made using the ` wrap ` method is different in two ways:
360
+
361
+ 1 . The result of the function call is memoized;
362
+ 2 . If the function call was successful, the value will automatically be an instance of ` Either.Right ` ;
363
+
364
+ ``` js
365
+ import Task from " https://deno.land/x/[email protected] /Task.js"
366
+
367
+ let count = 0 ;
368
+ const fetchUser = userID => Task .wrap (
369
+ _ => ++ count && fetch (` ${ URL } /users/${ userID} ` ).then (response => response .json ())
370
+ );
371
+
372
+ const user = fetchUser (userID);
373
+ const username = user .map (({ username }) => username);
374
+ const email = user .map (({ email }) => email);
375
+
376
+ // await user.run() === Either.Right({ email: "[email protected] ", username: "johndoe" })
377
+ // await username.run() === Either.Right("johndoe")
378
+ // await email.run() === Either.Right("[email protected] ")
379
+ // count === 1
380
+ ```
381
+
382
+ This implementation of Task is a valid [ ` Functor ` ] ( https://github.com/fantasyland/fantasy-land#functor ) ,
383
+ [ ` Applicative ` ] ( https://github.com/fantasyland/fantasy-land#applicative ) ,
384
+ [ ` Alternative ` ] ( https://github.com/fantasyland/fantasy-land#alternative ) and
385
+ [ ` Monad ` ] ( https://github.com/fantasyland/fantasy-land#monad ) .
386
+
288
387
## TypeScript
289
388
290
389
I will try to publish TypeScript type hint files for those who needs it.
291
390
So far, I've only implemented the Type factory functions.
292
391
293
392
``` ts
294
- // @deno-types="https://deno.land/x/functional/SumType.d.ts"
295
- import { factorizeType , factorizeSumType } from " https://deno.land/x/functional/SumType.js" ;
393
+ // @deno-types="https://deno.land/x/functional@v0.5.0 /SumType.d.ts"
394
+ import { factorizeType , factorizeSumType } from " https://deno.land/x/functional@v0.5.0 /SumType.js" ;
296
395
```
297
396
298
397
## Deno
0 commit comments