Skip to content

Commit c033897

Browse files
committed
feat: Move tests into the documentation examples
This makes sure that all functions have examples; since most examples are in the style of tests, redundant tests are removed. Fixes: #87 Fixes: #62 (Adds an appropriate test for uniq())
1 parent 4b370fe commit c033897

14 files changed

+1889
-1564
lines changed

README.md

+68-56
Original file line numberDiff line numberDiff line change
@@ -170,19 +170,17 @@ be extra arguments after the function (otherwise you end up with dangling argume
170170
while the function is usually also the first parameter you want to supply when currying:
171171

172172
```js,notest
173-
const {each} = require('ferrum');
174-
175-
// This is much more handy
176-
each([1,2,3], () => {
177-
...
178-
});
179-
180173
// This is not very handy because you might need to scroll down to find the last
181174
// argument; you will also need to scroll down to determine whether the call to
182175
// each is using currying
183176
each(() => {
184177
...
185178
}, [1,2,3]);
179+
180+
// This is much more handy
181+
each([1,2,3], () => {
182+
...
183+
});
186184
```
187185

188186
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
198196
standard library in the form of the [`|>` operator](https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Operators/Pipeline_operator).
199197

200198
```js
201-
const {sqrt} = Math;
199+
const { sqrt, floor } = Math;
202200
const {
203-
pipe, filter, uniq, map, mul, mapSort, identity,
201+
pipe, filter, uniq, map, mul, mapSort, identity, take,
204202
prepend, takeWhile, all, range, assertSequenceEquals,
203+
extend, plus, any,
205204
} = require('ferrum');
206205

207206
const a = pipe(
@@ -214,17 +213,16 @@ assertSequenceEquals(a, [3,9,15,21,33]);
214213

215214
// Very simple primality test
216215
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);
223219

224220
// Sequence of all prime numbers (calculated slowly)
225221
const primes = () => pipe(
226-
range(0, Infinity), // List of all positive integers
222+
range(0, Infinity),
227223
filter(isPrime));
224+
225+
assertSequenceEquals(take(primes(), 5), [2, 3, 5, 7, 11]);
228226
```
229227

230228
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
233231
let's first take away currying and the `pipe()` function itself:
234232

235233
```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');
238236

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));
240238
const primes = () => filter(range(0, Infinity), isPrime);
239+
240+
assertSequenceEquals(take(primes(), 5), [2, 3, 5, 7, 11]);
241241
```
242242

243243
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.
247247
Let's try another way to write down these functions:
248248

249249
```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');
252252

253-
const positiveIntegers = () => range(1, Infinity);
253+
const integers = () => range(1, Infinity);
254254
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);
258258
}
259-
const primes = () => filter(positiveIntegers(), isPrime);
259+
const primes = () => filter(integers(), isPrime);
260+
261+
assertSequenceEquals(take(primes(), 5), [2, 3, 5, 7, 11]);
260262
```
261263

262264
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.
278280
Finally, let's implement this in classic imperative style:
279281

280282
```js
281-
const {sqrt} = Math;
283+
const { sqrt } = Math;
282284

283285
const isPrime = (v) => {
284286
if (v < 2) {
285287
return false;
286288
}
287289

288-
for (let i=2; i <= sqrt(v); i++) {
290+
for (let i=0; i <= sqrt(v); i++) {
289291
if (v % i !== 0) {
290292
return false;
291293
}
@@ -295,7 +297,7 @@ const isPrime = (v) => {
295297
}
296298

297299
const primes = function *primes() {
298-
for (let i=2; true; i++) {
300+
for (let i=0; true; i++) {
299301
if (isPrime(i)) {
300302
yield i;
301303
}
@@ -335,9 +337,12 @@ This means that the values in iterators/sequences are only evaluated once they
335337
are needed:
336338

337339
```js
338-
const {map, plus, list} = require('ferrum');
340+
const { map, plus, list, assertSequenceEquals } = require('ferrum');
339341
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]);
341346
```
342347

343348
Try the above example with a couple of `console.log` statements and see what happens.
@@ -530,13 +535,15 @@ of traits at once.
530535
`Ferrum/Ops` provides all of the JS operators and some extra boolean operators as curryable functions.
531536

532537
```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);
540547
```
541548

542549
<a name="typing-utilities"></a>
@@ -546,19 +553,20 @@ Ferrum provides utilities for working with types that can be safely
546553
used with null and undefined.
547554

548555
```js
556+
const { strictEqual: assertIs } = require('assert');
549557
const {isdef, type, typename} = require('ferrum');
550558

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);
554562

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);
558566

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");
562570
```
563571

564572
The usual strategy of using `value.constructor` and `value.constructor.name`
@@ -568,20 +576,24 @@ yields errors for `null` & `undefined`.
568576
### Functional Utilities
569577

570578
```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');
572583

573584
// 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]);
580592

581593
// Auto currying
582594
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]);
585597
```
586598

587599
<a name="changelog"></a>

0 commit comments

Comments
 (0)