patternjavascriptMinor
JavaScript Functional Programming Bank Account, Shop and Customer
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
I also introduce a type
Next, a very impure function
Penultimately, I introduce a factory
`/ --------------------------------- 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
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 introducedPenultimately, 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.
I suggest putting
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?
Also, I'd like to think of functional programming as working with simple data structures and using functions to transform them. By this definition,
Since the values are plain objects and arrays, just feeding your data to
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.