@@ -170,19 +170,17 @@ be extra arguments after the function (otherwise you end up with dangling argume
170
170
while the function is usually also the first parameter you want to supply when currying:
171
171
172
172
``` js,notest
173
- const {each} = require('ferrum');
174
-
175
- // This is much more handy
176
- each([1,2,3], () => {
177
- ...
178
- });
179
-
180
173
// This is not very handy because you might need to scroll down to find the last
181
174
// argument; you will also need to scroll down to determine whether the call to
182
175
// each is using currying
183
176
each(() => {
184
177
...
185
178
}, [1,2,3]);
179
+
180
+ // This is much more handy
181
+ each([1,2,3], () => {
182
+ ...
183
+ });
186
184
```
187
185
188
186
Underscore.js does not support currying at all; lodash provides curried variants of their functions in an extra
@@ -198,10 +196,11 @@ Pipelines are conceptually the same as the highly successful pipes in bash; the
198
196
standard library in the form of the [ ` |> ` operator] ( https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Pipeline_operator ) .
199
197
200
198
``` js
201
- const {sqrt } = Math ;
199
+ const { sqrt , floor } = Math ;
202
200
const {
203
- pipe , filter , uniq , map , mul , mapSort , identity ,
201
+ pipe , filter , uniq , map , mul , mapSort , identity , take ,
204
202
prepend , takeWhile , all , range , assertSequenceEquals ,
203
+ extend , plus , any ,
205
204
} = require (' ferrum' );
206
205
207
206
const a = pipe (
@@ -214,17 +213,16 @@ assertSequenceEquals(a, [3,9,15,21,33]);
214
213
215
214
// Very simple primality test
216
215
const isPrime = (v ) => v > 1 && pipe (
217
- // Build the list of candidate factors
218
- range (0 , Infinity ), // List of all even integers >0
219
- takeWhile (x => x<= sqrt (v)), // Cut of the list at the square root of our input
220
- // Perform the division test
221
- map (x => v % x === 0 ),
222
- all); // NOTE: Due to lazy evaluation, dividing will stop at the first factor found
216
+ range (2 , floor (sqrt (v)) + 1 ),
217
+ map (x => v % x !== 0 ), // check that v is not divisible by x
218
+ all);
223
219
224
220
// Sequence of all prime numbers (calculated slowly)
225
221
const primes = () => pipe (
226
- range (0 , Infinity ), // List of all positive integers
222
+ range (0 , Infinity ),
227
223
filter (isPrime));
224
+
225
+ assertSequenceEquals (take (primes (), 5 ), [2 , 3 , 5 , 7 , 11 ]);
228
226
```
229
227
230
228
Learning to write algorithms in this way is not always easy, but it can be very rewarding
@@ -233,11 +231,13 @@ code of the prime sequence example changes as we take a way features from `Ferru
233
231
let's first take away currying and the ` pipe() ` function itself:
234
232
235
233
``` js
236
- const {sqrt } = Math ;
237
- const {all , map , takeWhile , filter , range } = require (' ferrum' );
234
+ const { sqrt , floor } = Math ;
235
+ const { all , map , takeWhile , filter , range , assertSequenceEquals , take , extend , plus } = require (' ferrum' );
238
236
239
- const isPrime = (v ) => v > 1 && all (map (takeWhile ( range (2 , Infinity ), x => x <= sqrt (v)), x => v % x = == 0 ));
237
+ const isPrime = (v ) => v > 1 && all (map (range (2 , floor ( sqrt (v))+ 1 ) , x => v % x ! == 0 ));
240
238
const primes = () => filter (range (0 , Infinity ), isPrime);
239
+
240
+ assertSequenceEquals (take (primes (), 5 ), [2 , 3 , 5 , 7 , 11 ]);
241
241
```
242
242
243
243
One way to work around the lack of currying and ` pipe() ` is to just put all our
@@ -247,16 +247,18 @@ and it does not help that subexpression cannot be properly documented any more.
247
247
Let's try another way to write down these functions:
248
248
249
249
``` js
250
- const {sqrt } = Math ;
251
- const {all , map , takeWhile , filter , range } = require (' ferrum' );
250
+ const { sqrt , floor } = Math ;
251
+ const { assertSequenceEquals , all , map , takeWhile , filter , range , take } = require (' ferrum' );
252
252
253
- const positiveIntegers = () => range (1 , Infinity );
253
+ const integers = () => range (1 , Infinity );
254
254
const isPrime = (v ) => {
255
- const fromTwo = range (2 , Infinity );
256
- const candidates = takeWhile (fromTwo , x => x <= sqrt (v));
257
- return v > 1 && all (map ( x => v % x === 0 ) );
255
+ const candidates = range (2 , floor ( sqrt (v)) + 1 );
256
+ const tests = map (candidates , x => v % x !== 0 )
257
+ return v > 1 && all (tests );
258
258
}
259
- const primes = () => filter (positiveIntegers (), isPrime);
259
+ const primes = () => filter (integers (), isPrime);
260
+
261
+ assertSequenceEquals (take (primes (), 5 ), [2 , 3 , 5 , 7 , 11 ]);
260
262
```
261
263
262
264
This is much better! The data flow is more clear and substeps can be documented again.
@@ -278,14 +280,14 @@ very useful, especially in large functions.
278
280
Finally, let's implement this in classic imperative style:
279
281
280
282
``` js
281
- const {sqrt } = Math ;
283
+ const { sqrt } = Math ;
282
284
283
285
const isPrime = (v ) => {
284
286
if (v < 2 ) {
285
287
return false ;
286
288
}
287
289
288
- for (let i= 2 ; i <= sqrt (v); i++ ) {
290
+ for (let i= 0 ; i <= sqrt (v); i++ ) {
289
291
if (v % i !== 0 ) {
290
292
return false ;
291
293
}
@@ -295,7 +297,7 @@ const isPrime = (v) => {
295
297
}
296
298
297
299
const primes = function * primes () {
298
- for (let i= 2 ; true ; i++ ) {
300
+ for (let i= 0 ; true ; i++ ) {
299
301
if (isPrime (i)) {
300
302
yield i;
301
303
}
@@ -335,9 +337,12 @@ This means that the values in iterators/sequences are only evaluated once they
335
337
are needed:
336
338
337
339
``` js
338
- const {map , plus , list } = require (' ferrum' );
340
+ const { map , plus , list , assertSequenceEquals } = require (' ferrum' );
339
341
const a = map ([1 ,2 ,3 ], plus (2 )); // At this point, no calculations have been performed
340
- const b = list (a); // This will actually cause the values of the a iterator to be calculated
342
+ const b = list (a); // This will actually cause the values of the `a` iterator to be calculatedA
343
+ assertSequenceEquals (a, []); // `a` is now exhausted and can no longer be used
344
+ assertSequenceEquals (b, [3 ,4 ,5 ]); // `b` can be used as often as we want
345
+ assertSequenceEquals (b, [3 ,4 ,5 ]);
341
346
```
342
347
343
348
Try the above example with a couple of ` console.log ` statements and see what happens.
@@ -530,13 +535,15 @@ of traits at once.
530
535
` Ferrum/Ops ` provides all of the JS operators and some extra boolean operators as curryable functions.
531
536
532
537
``` js
533
- const {plus , and , not , is , xor , map , list } = require (' ferrum' );
534
-
535
- list (map ([1 ,2 ,3 ], plus (2 ))); // => [3,4,5]
536
- and (true , false ); // => false
537
- not (1 ); // => false
538
- is (2 , 2 ); // => true
539
- xor (true , false ); // => true
538
+ const { strictEqual: assertIs } = require (' assert' );
539
+ const { plus , and , not , is , xor , map , list , assertSequenceEquals } = require (' ferrum' );
540
+
541
+ assertSequenceEquals (
542
+ map ([1 ,2 ,3 ], plus (2 )), /* => */ [3 ,4 ,5 ]);
543
+ assertIs (and (true , false ), /* => */ false );
544
+ assertIs (not (1 ), /* => */ false );
545
+ assertIs (is (2 , 2 ), /* => */ true );
546
+ assertIs (xor (true , false ), /* => */ true );
540
547
```
541
548
542
549
<a name =" typing-utilities " ></a >
@@ -546,19 +553,20 @@ Ferrum provides utilities for working with types that can be safely
546
553
used with null and undefined.
547
554
548
555
``` js
556
+ const { strictEqual: assertIs } = require (' assert' );
549
557
const {isdef , type , typename } = require (' ferrum' );
550
558
551
- isdef (0 ); // => true
552
- isdef (null ); // => false
553
- isdef (undefined ); // => false
559
+ assertIs ( isdef (0 ), /* => */ true );
560
+ assertIs ( isdef (null ), /* => */ false );
561
+ assertIs ( isdef (undefined ), /* => */ false );
554
562
555
- type (22 ); // => Number
556
- type (null ); // => null
557
- type (undefined ); // => undefined
563
+ assertIs ( type (22 ), /* => */ Number );
564
+ assertIs ( type (null ), /* => */ null );
565
+ assertIs ( type (undefined ), /* => */ undefined );
558
566
559
- typename (type (22 )); // => "Number"
560
- typename (type (null )); // => "null"
561
- typename (type (undefined )); // => "undefined"
567
+ assertIs ( typename (type (22 )), /* => */ " Number" );
568
+ assertIs ( typename (type (null )), /* => */ " null" );
569
+ assertIs ( typename (type (undefined )), /* => */ " undefined" );
562
570
```
563
571
564
572
The usual strategy of using ` value.constructor ` and ` value.constructor.name `
@@ -568,20 +576,24 @@ yields errors for `null` & `undefined`.
568
576
### Functional Utilities
569
577
570
578
``` js
571
- const {curry , pipe , filter , isdef , uniq , map , plus } = require (' ferrum' );
579
+ const {
580
+ curry , pipe , filter , isdef , uniq , map , plus ,
581
+ assertSequenceEquals , assertEquals ,
582
+ } = require (' ferrum' );
572
583
573
584
// Using pipe() + auto currying instead of chaining
574
- pipe (
575
- [0 ,1 ,2 ,null ,3 ,4 ,null ,5 ,1 ,3 ,2 ,null ,1 ,4 ],
576
- filter (isdef), // Filter out every null & undefined
577
- uniq, // Remove duplicates
578
- map (plus (2 ))); // Add two to each element
579
- // => [2,3,4,5,6,7]
585
+ assertSequenceEquals (
586
+ pipe (
587
+ [0 , 1 , 2 , null , 3 , 4 , null , 5 , 1 , 3 , 2 , null , 1 , 4 ],
588
+ filter (isdef), // Filter out every null & undefined
589
+ uniq, // Remove duplicates
590
+ map (plus (2 ))), // Add two to each element
591
+ /* => */ [2 , 3 , 4 , 5 , 6 , 7 ]);
580
592
581
593
// Auto currying
582
594
const pair = curry (' pair' , (a , b ) => [a, b]);
583
- pair (1 ,2 ); // => [1,2]
584
- pair (2 )(1 ); // => [1,2]
595
+ assertEquals ( pair (1 ,2 ), /* => */ [1 ,2 ]);
596
+ assertEquals ( pair (2 )(1 ), /* => */ [1 ,2 ]);
585
597
```
586
598
587
599
<a name =" changelog " ></a >
0 commit comments