- Composable Datatypes with Functions
- By Eric Elliott
- The Nuggets translation Project
- Permanent link to this article: github.com/xitu/gold-m…
- Translator: yoyoyohamapi
- Proofread by: IridescentMia Lampui
Writing composable data types with functions (Software Writing) (Part X)
(See Flickr for this image of the smoke squaring in Photoshop.)
Note: This is part 10 of the “Writing Software” series on learning functional programming and compositional software techniques from scratch in JavaScript ES6+. For the concept of software Composability, see Wikipedia Composability). There will be more exciting content, please look forward to! On a | < < < return to the first chapter
In JavaScript, the easiest way to do composition is function composition, and a function is just an object to which you can add methods. In other words, here’s what you can do:
const t = value= > {
const fn = (a)= > value;
fn.toString = (a)= > `t(${ value }) `;
return fn;
};
const someValue = t(2);
console.log(
someValue.toString() // "t(2)"
);Copy the code
This is a factory function t that returns an instance of a numeric type. Note, however, that these instances are not simple objects; they are functions, and composable functions. Given that we use t() for summation, it makes sense when we combine functions t() for summation.
First, suppose we establish some rules for t() (==== means “equal”) :
t(x)(t(0)) ==== t(x)
t(x)(t(1)) ==== t(x + 1)
In JavaScript, you can also compare with the.toString() method we created:
t(x)(t(0)).toString() === t(x).toString()
t(x)(t(1)).toString() === t(x + 1).toString()
We can also translate the above code into a simple unit test:
const assert = {
same: (actual, expected, msg) = > {
if(actual.toString() ! == expected.toString()) {throw new Error(`NOT OK: ${ msg }
Expected: ${ expected }
Actual: ${ actual }
`);
}
console.log(`OK: ${ msg }`); }}; {const msg = 'a value t(x) composed with t(0) ==== t(x)';
const x = 20;
const a = t(x)(t(0));
const b = t(x);
assert.same(a, b, msg);
}
{
const msg = 'a value t(x) composed with t(1) ==== t(x + 1)';
const x = 20;
const a = t(x)(t(1));
const b = t(x + 1);
assert.same(a, b, msg);
}Copy the code
At first, the test fails:
NOT OK: a value t(x) composed with t(0) ==== t(x)
Expected: t(20)
Actual: 20Copy the code
But we passed the test by following three steps:
- The function
fn
intoadd
Function, which returnst(value + n)
,n
Represents an incoming parameter. - For the function
t
Add a.valueOf()
Methods that make newadd()
The function acceptst()
Returns an instance as an argument.+
The operator is usedn.valueOf()
As the second operand. - use
Object.assign()
将toString()
..valueOf()
Method assigned toadd()
function
Steps 1 to 3 are combined to obtain:
const t = value= > {
const add = n= > t(value + n);
return Object.assign(add, {
toString: (a)= > `t(${ value }) `.valueOf: (a)= > value
});
};Copy the code
After that, the test passes:
"OK: a value t(x) composed with t(0) ==== t(x)"
"OK: a value t(x) composed with t(1) ==== t(x + 1)"Copy the code
Now, you can combine t() using function combinations to achieve the summation task:
// Top down function combination:
const pipe = (. fns) = > x => fns.reduce((y, f) = > f(y), x);
// The summation function is the initial value that pipeline passes in
// Curried pipelines are more reusable, we can delay passing arbitrary initial values
const sumT = (. fns) = >pipe(... fns)(t(0));
sumT(
t(2),
t(4),
t(- 1)
).valueOf(); / / 5Copy the code
This applies to any data type
The strategy above will help you no matter what your data looks like, as long as there is a meaningful combination of operations. For lists or strings, a composition can perform concatenation. For DSP (digital Signal processing), composition is the sum of signals. Of course, other actions can also give you the desired results. So the question is, which operation best reflects the idea of composition? In other words, which operation would benefit more from the following code organization:
const result = compose(
value1,
value2,
value3
);Copy the code
A combinable currency
Moneysafe is an open source library that implements this composable, functional data type style. The JavaScript Number type cannot accurately represent cents:
1. + 2.= = =3. // falseCopy the code
Moneysafe solves this problem by elevating the dollar type to the cent type:
npm install --save moneysafeCopy the code
After:
import{$}from 'moneysafe';
$(1.) + $(2.) = = = $(3.).cents; // trueCopy the code
Ledger syntax takes advantage of Moneysafe’s ability to elevate generic values to composable functions. It exposes a simple suite of functions called ledger:
import{$}from 'moneysafe';
import { ?, subtractPercent, addPercent } from 'moneysafe/ledger';
?(
$(40) and $(60),
// Subtract the discount
subtractPercent(20),
/ / tax
addPercent(10)
).$; / / 88Copy the code
The return type of this function is the promoted money type. The return value exposes a.$getter method that rounds the internal floating point value to dollars.
The result is an intuitive reflection of performing Ledger-style gold calculations.
Test if you really understand
Clone Moneysafe warehouse:
git clone [email protected]:ericelliott/moneysafe.gitCopy the code
Perform the installation process:
npm installCopy the code
Run unit tests and monitor console output. All use cases pass:
npm run watchCopy the code
Open a new terminal and remove the Moneysafe implementation:
rm source/moneysafe.js && touch source/moneysafe.jsCopy the code
Go back to the previous terminal window and you will see an error.
Your task now is to implement Moneysafe.js from scratch and pass all the tests with the help of the unit test output and documentation.
Next: JavaScript Monads makes Everything easy
The following
Want to learn more about JavaScript functional programming?
Learn Javacript with Eric Elliott, now or never!
Eric Elliott is the author of “Write JavaScript Apps” (O ‘Reilly) and “Learn JavaScript with Eric Elliott.” He has contributed to many companies and organizations, such as Adobe Systems, Zumba Fitness, The Wall Street Journal, ESPN and BBC, and is a top artist for many organizations, Including but not limited to Usher, Frank Ocean and Metallica.
He spent most of his time in the San Francisco Bay Area with the most beautiful women in the world.
The Nuggets Translation Project is a community that translates quality Internet technical articles from English sharing articles on nuggets. The content covers Android, iOS, React, front-end, back-end, product, design and other fields. If you want to see more high-quality translation, please continue to pay attention to the Project, official Weibo, Zhihu column.