HiveBrain v1.2.0
Get Started
← Back to all entries
patternjavascriptMinor

JavaScript Functional Programming Bank Account, Shop and Customer

Submitted by: @import:stackexchange-codereview··
0
Viewed 0 times
javascriptprogrammingandaccountbankshopfunctionalcustomer

Problem

Motivation

I have worked extensively with JavaScript and have grown fond of some of its functional aspects. This got me wondering how one could implement the canonical example of a bank account - and a small application using it - in a purely functional manner in ES6.

Code

I introduce three types Account, Shop and Customer, all of which are defined through their respective factories. The resulting objects contain impure log methods, but I am unsure as to how I could get rid of that.
I also introduce a type Iterable, that models a series of iterations.

Next, a very impure function iterateOnClick, which has the side effect of having each click on the buy-button execute one iteration of a given Iterable, is introduced

Penultimately, I introduce a factory makePurchaseIteration to create an Iterable which will let a customer buy kittens until they run out of funds and finally, a main function is defined and run which simply sets up the desired iteration.


`/ --------------------------------- TYPES ---------------------------------- /
/*
* Account
* Models a bank account.
*
* Fields:
* balance: number
* Current amount of dollars in account.
* Methods:
* deposit: (amount: number) => Account
* Returns Account with balance increased by ${amount}.
* withdraw: (amount: number) => Account
* Returns Account with balance decreased by ${amount}.
* log: () => undefined
* Side effect: Logs the current balance.
*
* Factories:
* Account: () => Account
* Creates Account with balance 0.
* Account: (initialAmount: number) => Account
* Creates Account with balance ${initialAmount}.
*/
const Account = (() => {
const makeAccount = (slips) => {

// Fields
const balance = slips.reduce((sum, value) => sum + value);

// Methods
const deposit = (amount) => makeAccount([...slips, amount]);
const withdraw = (amount) => makeAccount([...slips, -amount]);
const log = () => cons

Solution

In terms of documentation, JSDoc is a formal syntax for documentation. Some IDEs even use the JSDoc syntax as a pseudo-type system, giving you warnings when your types don't match up.

Another problem is your excessive usage of arrow functions. One problem with arrow functions is that in some platforms, their names won't appear in the stack trace. They will appear as "anonymous function". Although you will be provided the file name with line and column numbers, having that name is a leg up when debugging. Use function declarations instead.

const Account = (() => {
  const makeAccount = (slips) => {
    ..
  };

  // Factory
  return (initialAmount = 0) => makeAccount([initialAmount]);
})();


I suggest putting makeAccount outside Account. You're getting no benefit from it, you're also not closing over some value. I believe you're only using it to default the function to a value.

Default values often make functions hard to explain. Instead of blowing up when nothing was given, it performs an operation as if something was given. Why not give the value in the first place to make the function straightforward?

return Object.freeze({
  // Fields
  balance,

  // Methods
  deposit,
  withdraw,
  log,
});


Object.freeze is unnecessary. Although it gives you extra protection from outside forces trying to alter your data, if it's just your code and you're writing it in a non-mutating fashion, then this is unnecessary.

Also, I'd like to think of functional programming as working with simple data structures and using functions to transform them. By this definition, log becomes out of place since it's not a value related to your data but a handy logging function.

Since the values are plain objects and arrays, just feeding your data to console.log, optionally serializing them with JSON.stringify should do. No need for a log method in your data.

Code Snippets

const Account = (() => {
  const makeAccount = (slips) => {
    ..
  };

  // Factory
  return (initialAmount = 0) => makeAccount([initialAmount]);
})();
return Object.freeze({
  // Fields
  balance,

  // Methods
  deposit,
  withdraw,
  log,
});

Context

StackExchange Code Review Q#146148, answer score: 2

Revisions (0)

No revisions yet.