Skip to content

Commit

Permalink
feat: implement subtract as an operator function
Browse files Browse the repository at this point in the history
  • Loading branch information
customcommander committed Jan 3, 2022
1 parent cf2b38f commit 608b408
Show file tree
Hide file tree
Showing 7 changed files with 127 additions and 8 deletions.
22 changes: 15 additions & 7 deletions api.jq
Original file line number Diff line number Diff line change
Expand Up @@ -6,21 +6,28 @@ def function_name:
.meta.filename
| rtrimstr(".js");

# Search for words that can be linked to the glossary.
def glossary:
gsub("(?<w>predicates?)"; "[\(.w)](../manual/glossary.md#predicate)"; "i")
| gsub("(?<w>`?nil`?)"; "[\(.w)](../manual/glossary.md#nil)"; "i")
| gsub("(?<w>logical (true|truth))"; "[\(.w)](../manual/glossary.md#logical-truth)"; "i")
| gsub("(?<w>logical (false|falsity))"; "[\(.w)](../manual/glossary.md#logical-falsity)"; "i")
| gsub("(?<w>operator function)"; "[\(.w)](../manual/glossary.md#operator-function)"; "i")
| gsub("(?<w>functional placeholder)"; "[\(.w)](../manual/glossary.md#functional-placeholder)"; "i");

def is_curried:
(.tags // [])
| contains([{title: "curried"}]);

def is_operator:
if (.tags // [] | contains([{title: "operator"}]) | not) then null else
"This is an operator function." | glossary
end;

def is_transducer:
(.tags // [])
| contains([{title: "transducer"}]);

# Search for words that can be linked to the glossary.
def glossary:
gsub("(?<w>predicates?)"; "[\(.w)](../manual/glossary.md#predicate)"; "i")
| gsub("(?<w>`?nil`?)"; "[\(.w)](../manual/glossary.md#nil)"; "i")
| gsub("(?<w>logical (true|truth))"; "[\(.w)](../manual/glossary.md#logical-truth)"; "i")
| gsub("(?<w>logical (false|falsity))"; "[\(.w)](../manual/glossary.md#logical-falsity)"; "i");

def params:
if (.params | not) then null else
.params |
Expand Down Expand Up @@ -70,6 +77,7 @@ def description:
map(select(.longname == "module.exports") | {
function_name: function_name,
is_curried: is_curried,
is_operator: is_operator,
is_transducer: is_transducer,
summary: summary,
description: description,
Expand Down
39 changes: 38 additions & 1 deletion docs/manual/glossary.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
# Glossary

List of terms and concepts used throughout the library.
!!! info

List of terms and concepts used throughout the library.

## functional placeholder

A special value used in curried functions that will be substituted with the real value later on.
Useful with [operator functions](./glossary.md#operator-function).

## logical truth

Expand All @@ -15,6 +22,36 @@ Any value that is either `false` or [nil](./glossary.md#nil) constitutes logical

Either `null` or `undefined`.

## operator function

An operator function is a binary function with *left* and *right* sections.

To illustrate this concept we will use the operator function [`subtract`](../api/subtract.md)
which is the functional equivalent of `a - b`.

When called with one argument the operator function assumes it has been given the *right* section and returns
a function that takes the *left* section:

```javascript title="Similar to x - 2"
const sub2 = subtract(2);

sub2(44);
//=> 42
```

When called with two arguments **and** the second argument is the
[functional placeholder](./glossary.md#functional-placeholder), the operator function
assumes it has been given the *left* section and returns a function that takes the *right* section.

```javascript title="Similar to 44 - x"
const subFrom44 = subtract(44, __);

subFrom44(2);
//=> 42
```

Operator functions in this library have been inspired by this [answer from Stack Overflow](https://stackoverflow.com/a/25720884/1244884).

## predicate

A predicate is a function that uses [logical truth](./glossary.md#logical-truth)
Expand Down
6 changes: 6 additions & 0 deletions docs/rtfm.ejs
Original file line number Diff line number Diff line change
Expand Up @@ -30,6 +30,12 @@ const find_other_transducers = () => {
This function is curried.
<% } %>
<% if (DATA.is_operator) { %>
!!! tip
<%- DATA.is_operator %>
<% } %>
<% if (DATA.is_transducer) { %>
!!! tip
Expand Down
10 changes: 10 additions & 0 deletions src/__.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
/**
* @license MIT
* @copyright (c) 2022 Julien Gonzalez <hello@spinjs.com>
*/

/**
* @summary
* Functional placeholder.
*/
module.exports = Symbol();
20 changes: 20 additions & 0 deletions src/_operator.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
/**
* @license MIT
* @copyright (c) 2022 Julien Gonzalez <hello@spinjs.com>
*/

const __ = require('./__');

/**
* @param {?} a
* @param {?} b
* @return {?}
*/
module.exports = fn => function (a, b) {
const nargs = arguments.length;
// fn(b) -> a => fn(a, b)
if (nargs == 1) return x => fn(x, a);
// fn(a, __) -> b => fn(a, b)
if (nargs > 1 && b === __) return x => fn(a, x);
return fn(a, b);
};
24 changes: 24 additions & 0 deletions src/subtract.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
/**
* @license MIT
* @copyright (c) 2022 Julien Gonzalez <hello@spinjs.com>
*/

const op = require('./_operator');

/**
* @summary
* Functional equivalent of `a - b`.
*
* @example
* // Remove `2` from all numbers in the list:
* map(subtract(2))([3,4,5]);
* //=> [1,2,3]
*
* @operator
* @function
* @param {number} a
* @param {number} b
* @return {number}
* @see __
*/
module.exports = op((a, b) => a - b);
14 changes: 14 additions & 0 deletions test/subtract.test.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
const testcheck = require('./_check');
const {subtract: sut, __} = require('..');

testcheck('subtract(a, b)',
['number', 'number'], (a, b) =>
sut(a, b) === a - b);

testcheck('subtract(b)(a)',
['number', 'number'], (a, b) =>
sut(b)(a) === a - b);

testcheck('subtract(a, __)(b)',
['number', 'number'], (a, b) =>
sut(a, __)(b) === a - b);

0 comments on commit 608b408

Please sign in to comment.