jmbothe/calculator-MVC

A basic calculator learning exercise
Fetching latest commit…
Cannot retrieve the latest commit at this time.
Type Name Latest commit message Commit time
Failed to load latest commit information.
assets
dist
scripts
styles
.babelrc
.gitignore
index.html
package.json

Description

How is this calculator like the basic iOS calculator?

• It follows the basic order of operations.
• It displays different subtotals based on which operator the user has selected.
• It displays numbers in an attractive and dynamic format.

How does this project follow the MVC design pattern?

• The calculatorMVC namespace has three properties: model, view, and controller.
• To model implements the raw functionality of the calculator.
• The view is responsible for how the calculator appears in the browser on the device.
• The controller mediates communication between the user and the model, and between the view and the model.

Highlights and Discussion

Coding the order of operations with currying and partial application.

I imagine there are a number of ways to successfully code a set of functions that will handle the basic order of operations, but I wanted to implement something that was new and challenging, so I decided to make currying and partial application central to the functionality of the calculator's model. For those who don't know (or for those who know, but who wish to sit back and enjoy watching a newb struggle to explain succinctly), currying is a technique whereby a function that takes multiple arguments is rewritten as a series of nested functions that each take one argument. Partial application is the related technique of fixing some of the arguments of those nested functions, and returning a new function that takes just the arguments that have yet to be fixed. Currying and partial application allow this calculator to follow the order of operations by suspending the current operator and ONE of its operands within a partially applied function whenever the subsequent operator is of higher precedence. Once the higher-precedence operation is complete, its return value is passed as the final argument to the partially applied function, which then completely evaluates the pending lower-precedence expression. In addition to maintaining the order of operations, this setup also allows for the display of alternative subtotals in the event that the user consecutively selects different operators.

Modules with private members.

I had been reading through @getify's Scope & Closures, and of the thirty-or-so-percent that I actually understood, I was intrigued by the idea of restricting access to variables by making them private members of modules. Although this calculator is incredibly simple, and security is not really an issue, I thought it would be a great learning exercise to try to implement some modules with private members here. At the very least, including private members helped to control the side effects of code that necessarily contains quite a number of conditional paths (see below for more on conditionals). This pattern was particularly useful for coding the `expressionModule`, as I was was able to reuse it THREE TIMES!

Separation of concerns.

This is still a new concept for me, but one thing I have come to understand is that there are different ways to approach the separation of concerns. For this project, the separation of concerns is measured by the fact that neither the model nor the view contains a reference to the other, nor to the controller. All interactions are mediated by the controller. I am aware that this is not a universally accepted standard--I imagine this sort of rigid commitment carries some of its own baggage--but as a first-time learner, it was actually the easiest way to continually monitor and stay true to the separation of concerns: if a reference shows up somewhere it doesn't belong, that means its time to rewrite!

An important part of reducing the complexity of code (and increasing its readability/reusability/refactorability) is reducing the number of conditional paths it could potentially follow. I came face-to-face with this truth while debugging earlier implementations of the calculator's model. It is truly dizzying trying to chase bugs through a seemingly endless web of connections between different possible code states. It was a continuous struggle to manage all the paths the code was taking, and it ultimately boiled down to a concerted effort to cull the bulk of those paths. Still, although the current calculator is functional and fast, I am not entirely happy with how many conditional paths remain. But it's important to keep in mind that any code that does anything interesting will inevitably have SOME malleable state and associated conditionality to it. I simply tried my best to stick to only the essential paths, contain and restrict their side effects, and make them intelligible for readers/users.

Contributing

Your contributions are very welcome! The best thing you could possibly do is break this code and then let me know about it. Contact me or open an issue to discuss potential changes/additions.

Author

• Jeff Bothe, @jmbothe

Inspiration