App 2: Lessons Learned

Having gone through the process of writing the Word Count application and then the To-Do List app, let’s recap what you’ve seen.

Our rules for programming

When I started, I said that these were our rules for programming, that we’d only use:

  • Immutable variables
  • Immutable data structures
  • Pure functions
  • Expression-oriented programming
  • Error-handling data types
  • Try any time we accessed the outside world

Because this example only accesses a flat-file database (that we implemented), I also asked you to use your imagination to think that we were either accessing a remote database or remote REST service, both of which could be inaccessible at any time.

I violated a couple of those rules for the reasons I gave at that time, i.e., that I wanted to make sure that I wrote code that Scala beginners could understand. But if you want to see the correct Scala solution, see the “Using” lesson in the Appendix.

But other than those violations, I followed the rules to a tee, and you saw how the application came together.

Check-in time

At this time my main thought is that when this book started, you probably weren’t used to using data types very much, but now that we’ve written two small applications, you’re probably much more used to them. So this is a good time to ask, do you feel that there are benefits to all of the techniques shown, and if so, what they are?

Benefits

To me, the benefits I demonstrated are:

  • You know that you can trust pure functions; their type signatures cannot lie.
  • When I look at a function signature and see Option, Try, or Either as its return type, I instantly think, “Something can go wrong in this function”.
  • When I see a function signature that (a) takes no input parameters or (b) has the Unit return type, I know that it’s performing some sort of side effect.
  • Even if I don’t know the name of a function, when I look at its signature, I can get a good idea of what it can possibly do.

Furthermore:

  • Reading EOP can be as simple as code gets: you bind the result of a pure function to an algebraic variable. The code is easy to write, read, and test.
  • Writing code like this is like writing algebra, or creating a blueprint.
  • Combining expressions together is just like combining algebraic equations.

Other things I know from experience that I haven’t shown here are:

  • I know that immutable variables and immutable data structures are safe to use in parallel and concurrent programming.
  • I know that pure functions are easier to test than methods that perform side effects.

In regard to that point about testing, an impure function may do any of these things:

  • Read input, without warning callers.
  • Write output, without warning callers.
  • Mutate the parameters they are given.
  • Mutate variables inside their class, or global parameters.
  • Access other variables through side doors.

Because of those, it’s much harder to set up their state in a testing environment: it’s hard to know exactly what else they might be accessing.

Conversely, with a pure function — where you know that the only variables it accesses come in through the front door, and it has no side effects — you know that setting up its test environment will be much easier.

Key point

And now a key point of all of this is that if you’re still with me — if everything I’ve shown makes sense — you are really, really close to functional programming. Probably much closer than you’re thinking.

We just need to take a few more steps, which we’ll do next.