• 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)

Cubes to Smoke — MattysFlicks — (CC BY 2.0)

(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:

  1. The functionfnintoaddFunction, which returnst(value + n)nRepresents an incoming parameter.
  2. For the functiontAdd a.valueOf()Methods that make newadd()The function acceptst()Returns an instance as an argument.+The operator is usedn.valueOf()As the second operand.
  3. useObject.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.