Object-oriented or functional? Why not both with Scala?

I think this is a city bike, though the seat seems kind of high. Photo by Mikkel Bech on Unsplash

Just as there are pundits proclaiming the death of the Java programming language specifically, there are pundits proclaiming the death of object-oriented programming in general.

There are problems with object-oriented programming, but maybe a complete discard of object-oriented programming in favor of functional programming is not the answer. Maybe the answer is the hybrid approach of Scala.

A week ago, before Thanksgiving, I gave a talk at Detroit Labs on this very topic, titled “Dipping a Toe in Functional Programming with Scala.” I got a lot of good feedback.

For one thing I should have mentioned closer to the beginning that I am not a Scala expert. Part of the beauty of Scala is that you can start out in it using only its object-oriented features and very gradually start using its functional capabilities.

I misjudged the proper balance of certain topics for the Audience; some things I should have said more about, some less. Also, some of the things I put in speaker notes should have gone on the slides themselves, and vice-versa.

I am thankful to Detroit Labs for giving me the opportunity to give this talk. If I give this talk somewhere else, it’ll be much better because presenting it at Detroit Labs allowed me to see very clearly what works, what doesn’t work, what needs to be tweaked, what needs to stay the same, etc.

Because this article covers material for an hour-long talk, it’s not a quick read. If you want to install Scala on your system to follow along, you might want to figure that into the reading time.

What now follows is not a transcript of my talk, but perhaps a template to give this talk again in a similar context to the talks at Detroit Labs (there won’t be a talk at Detroit Labs in December but they’ll resume in January with a slate of new speakers).

On the plus side, with this article you can breeze through the familiar and slow down for the unfamiliar, whereas for the talk I had to keep going even if I worried that a small minority was not understanding what I was saying.

On the minus side, I might take hours or even days to answer questions posted in the comments, whereas at the talk people could raise their hands to ask me to clarify something before I moved on to the next slide.

I do think that I was right not to go too in depth on the history of Scala. The most salient point is that Martin Odersky, the inventor of Scala, is also the co-author of an early Java compiler, and he was a driving force to add generics to Java.

So if you’ve ever written a line like this one in Java:

thank Martin Odersky.

A couple of years ago, our own Charles Scalfani here on Medium wrote “Goodbye, Object Oriented Programming.” He listed several problems with object-oriented programming, like the banana monkey jungle problem, the diamond problem, the fragile base class problem, etc.

Scalfani’s most important point is, I think, that object-oriented programming, because of its emphasis on inheritance hierarchies rather than containment hierarchies, does not really reflect the real world.

“But the real world is filled with Containment Hierarchies. A great example of a Containment Hierarchy is your socks. They are in a sock drawer which is contained in one drawer in your dresser which is contained in your bedroom which is contained in your house, etc.” — Charles Scalfani

In this sock example, it doesn’t quite matter if a Sock is subclassed as a TubeSock or an AnkleSock or whatever, but that it is in a Drawer in a Dresser. And it doesn’t matter so much if the Dresser is some kind of Cabinet.

When you first learned about object-oriented programming, you might have been given an exercise in which you had to create an inheritance hierarchy of bicycles.

You might have created an elaborate hierarchy of several levels of inheritance with Bicycle, MountainBike, DownhillBike, TrailBike, CityBike, PennyfarthingBicycle, etc.

Even in a shallow inheritance hierarchy, having to cast objects up or down the hierarchy can quickly grow tiresome. It’s something you might not have to deal with if you just do the exercise and then move on.

If you took the basic inheritance exercise further, though, you might run into annoying situations that make you question the logic of your inheritance hierarchy design.

For example, you know that a particular bike is a TrailBike, but because you initialized it as a MountainBike, you have to cast it to a TrailBike just to access one little property or subroutine unique to TrailBike.

To give another example, suppose bike is initialized as a TrailBike, and then you run into problems when you need the computer to see it as a MountainBike.

Another inheritance hierarchy you may have seen given as an exercise is one of stringed, fretted instruments. Like Guitar, Ukulele, Banjo, Mandolin, etc. You might have created an ElectricalInstrument interface that any subclass of StringedInstrument can implement.

Ukulele. Its four strings are generally tuned G-C-E-A. Photo by Samuel Ramos on Unsplash

And you might have also created a string class (but called it something other than String), so that when you construct a StringedInstrument you have to pass it an array of strings (six for a Guitar, four for a Ukulele, four pairs for a Mandolin, etc.).

To change the tuning of a StringedInstrument object, you’d probably have to access the strings’ retune() procedure through StringedInstrument. That’s a containment hierarchy, so this goes to Scalfani’s point.

In a practical situation, you might not really care what the inheritance is on your ElectricDescantBalalaika, only that you’re able to tune it and play it without unnecessary hassle.

Maybe object-oriented programming doesn’t always reflect mathematical objects either.

For example, in a program that I’m working on, an algebraic integer calculator (source and tests are available from GitHub) I need to distinguish between numbers of the form a + b√2 and numbers of the form a + b√3, to give just two examples of sets of numbers the program deals with.

Here a and b are from the familiar set of integers which mathematicians often denote by the symbol Z. The set Z is infinite, and so are the sets Z[√2] (all numbers of the form a + b√2) and Z[√3] (all numbers of the form a + b√3).

In my program, there is the class QuadraticRing which implements the IntegerRing interface, and the classes RealQuadraticRing and ImaginaryQuadraticRing, which extend QuadraticRing.

Since these objects represent infinite sets, it’s obvious that your computer can’t hold every number in any of those. This is of course a purely philosophical problem which we overcome by accepting that there are practical limitations on how closely an object in our computer can model something in reality or in our minds.

In my program you will find a far more serious problem that you might not think is as easy to dismiss as being purely philosophical. In the aforementioned QuadraticRing, you will find:

Seems reasonable enough.

So far so good. But then:

So this function that supposedly returns a double never actually does so when called on an instance of ImaginaryQuadraticRing, because it always throws an exception.

Does my practical need to access the number √d/i when d is negative outweigh the inelegance of a function overridden to always throw an exception instead of giving a result?

I suppose I could redesign this so that getRadSqrt() can only be called on instances of RealQuadraticRing. And that becomes another headache in an inheritance hierarchy that is not very deep.

This might say more about my ability to design inheritance hierarchies than it does about the soundness of object-oriented programming as a concept. But if it’s not intuitive, maybe it’s not a very useful paradigm.

Should we just completely get rid of object-oriented programming and switch over to functional programming? I say no, because of the great investment we have all made in object-oriented programming.

At my Detroit Labs talk, I asked the attendees how many of them work with object-oriented programming for their jobs. Almost all of them raised their hands. For better or worse, object-oriented programming is here to stay.

One way to leverage our years of expertise on object-oriented programming to learn functional programming is to use a programming language with a hybrid approach, like Scala.

The nice things about Scala is that it can run on the Java Virtual Machine (JVM), it can use everything in the Java Development Kit (JDK), and it can probably use any Java third-party library. For the most part, Scala classes and traits interoperate smoothly with Java classes and interfaces.

There are a couple of small caveats: unlike the Groovy compiler, the Scala compiler can’t compile Java source code, but it still needs to see it if it’s not already compiled to a class file or a JAR; and IntelliJ apparently can’t auto-generate Scala, so it will auto-generate a Java test class for a Scala source class.

To follow along with the rest of this article, I strongly recommend that you install the Scala binaries, including the Scala REPL, on your system, if you don’t have them already. Go to https://www.scala-lang.org/download/ and scroll down to “Other ways to install Scala.”

You can also follow along in IntelliJ by having it download the Scala plugin. I haven’t yet figured out how to enable Scala in NetBeans.

With the Scala binaries on your system and the path environment variable adjusted, you can run the Scala REPL from the command line with the command scala. For much more detail about the Scala REPL, see my article from a few months ago.

The Scala REPL will work if you don’t have scala\bin in your system path, but the Scala compiler will not (you’ll get a not very helpful error message about an unexpected toolcp).

The foregoing doesn’t apply if IntelliJ takes care of compiling Scala for you, or maybe if you’re using the Scala Build Tool.

Scala’s functionality is paradoxically enabled by the fact that everything in Scala is an object. Objects are objects. Primitives are objects. Functions are objects. In the Scala REPL, or in Scastie in worksheet mode, you can try this out:

I admit I don’t fully understand that last one, but the important point here is that the function Math.sqrt(), which comes from Java, has a class in Scala and is therefore an object (the underscore character clarifies to Scala that we’re referring to the function, not to one of its possible outputs, like Math.sqrt(2), which is then of course of class [Double] = double).

Let’s take a step back from functional programming and talk about operator overloading. If you’re a C# programmer (there was precisely one at my talk), this is no big deal.

Like Java, C# is object-oriented and it runs on a virtual machine, but like C++, C# has always had operator overloading.

To illustrate operator overloading at my talk, I used the example of Fraction, which, if you compile, you can load into the Scala REPL with the command line option -cp.

That’s a Java class I wrote, had NetBeans compile into a JAR, then loaded that into the Scala REPL. Then I could instantiate objects of type Fraction.

It sure would be nice to be able to use the plus, minus, times and divides operators directly.

So let’s rewrite this as a Scala class. We could keep all the semicolons, but to help differentiate between Java and Scala, I will omit them.

Note that the default constructor has to be close to the top. Also note that the “= 1L” bit allows us to declare a Fraction equal to an integer by leaving out the denominator when we instantiate, instead of explicitly having to give a denominator of 1.

Functions can also have default parameters defined in this manner. One more thing to take note of before moving on: return at the end of + is not needed. The value at the end of a block is the value of the block as a whole.

With that compiled and loaded into the Scala REPL, we can now do this:

Technically, though, this is not actually operator overloading, but rather the result of the combination of Scala allowing operator characters as function names and Scala allowing infix notation.

With the JAR compiled from the Java sources loaded into the Scala REPL, we can very well do things like this:

Likewise with the JAR compiled from the Scala sources we can do:

Though then again, in C++ it would be valid to write oneHalf.operator+(twoThirds) (according to the Microsoft page on the topic). So I guess Scala does technically have operator overloading after all.

Operator overloading is not specifically a functional concept, as our colleagues with C++ or C# experience can tell us, but it can certainly help functional programming.

At this point in the talk I should have gone on to talk about passing functions to standard Scala functions. A really good one to start with is map(), which will probably be familiar to JavaScript programmers.

In his talk at Detroit Labs about JavaScript anti-patterns last month, James York mentioned map() as a generally preferable alternative to the foreach loop, both of which are also available in Scala.

Of course in Scala there is a wider variety of what you can use map() on. Let’s get some kind of array or collection to hold the first one hundred positive integers in the Scala REPL:

Now, let’s convert that to numbers of the form 4n + 1.

Oops, I forgot this is not Wolfram Mathematica, I can’t use an implied multiplication operator, it has to be made explicit.

That’s more like it. Now let’s do a more elaborate example in which we have a collection of fractions, multiply by them each by 4 and add 1. For this one we’ll need to take care of some overhead.

I defined four and one of type Fraction so as to not deal with operator overloading between Int and Fraction; I haven’t figured out how to do it yet.

I’m not quite sure what happened with res11 there towards the end, but I’m satisfied this has given the right results and that it would hold up under the scrutiny of unit testing.

Now I return to the content of my talk last week. How do we write our own functions that take functions as parameters? The best example I can think of for that is implementing the Euclidean GCD algorithm.

At this point in the talk, I asked for someone in the Audience to tell me what gcd(−27, 18) is. William Rusnack, who is very knowledgeable about functional programming in Haskell, said 9, which is the right answer.

I’m sure almost everyone else, or maybe all of them, also thought of the correct answer. I’m no neurologist, but I think they came up with the answer by noticing that both −27 and 18 are divisible by 9.

A computer, on the other hand, would probably have to use the Euclidean algorithm even for this very simple example: −27 = −2 × 18 + 9, then 18 = 2 × 9 + 0, and the remainder 0 lets the computer know it has gotten the answer.

In programming this, we almost always take it for granted that the Euclidean function for Z is usually the absolute value function: |n| = n if n is 0 or positive, |n| = −n if n is negative.

It’s simply the most practical choice, from the heyday of FORTRAN and COBOL to the heyday of Pascal, C and C++, to today with Kotlin, Haskell, and maybe even Malbolge… just kidding on that last one.

Although −27 < 18, as far as the Euclidean algorithm is concerned, it matters more that 18 is closer to 0 than −27 is. The absolute value function tells us that |−27| > |18|.

The square function also works, since (−27)² > 18². The problem with the square function is that we could run into overflow issues, as in, for example, euclideanGCD(Integer.MIN_VALUE, Integer.MAX_VALUE).

These are the mathematical requirements for f(n) to be a valid Euclidean function in a given domain of numbers R such as Z:

  • f(n) maps all numbers of R to N⁰ (meaning the positive integers and 0, so Z without the negative integers).
  • If d is a divisor of n, then f(d) ≤ f(n).
  • f(n) = 0 if and only if n = 0.

It would be nice if whenever d is a divisor of n we likewise have that f(d) is a divisor of f(n). And though that is often the case, it is actually not a mathematical requirement. I’ll come back to this point later on.

For the third requirement, we may add, as a practical matter, the caveat that numbers like Integer.MIN_VALUE could lead to incorrect zeroes with the square function.

Though the absolute value function could also be problematic when applied to int values.

For my purpose here, however, I’m dealing with numbers closer to 0, so I’m not too bothered by these potential errors in my Euclidean GCD implementation; I don’t plan to write tests for potential overflows.

In Java, if we want to program the Euclidean GCD algorithm with a different Euclidean function, we have a lot of rewriting to do. But in Scala, we can simply pass it the function we want to use as needed, with just a little rewriting.

This stub illustrates the syntax:

So now we can pass euclideanGCD() any function that takes in an Int and returns an Int, though of course we also have to flesh out the stub to actually use that function.

For this part, it might be better to follow along in IntelliJ rather than the Scala REPL, since it’s a lot easier to import JUnit into IntelliJ than into the Scala REPL.

There is such a thing as ScalaTest, but I haven’t figured out how to use it yet. Maybe as I get more proficient with functional programming, I will find that JUnit doesn’t quite cut it for testing Scala. But for now, JUnit is just fine.

Like I mentioned before, IntelliJ can’t auto-generate Scala code for use with JUnit. But you can just right-click on the test folder as shown in IntelliJ and create a new Scala file there. I’m guessing it’s similar in NetBeans if Scala is properly set up for that integrated development environment (IDE).

Okay, so our first test should be, by the tenets of test-driven development, rather simple, and easy to make it go from failing to passing.

The void in Java is Unit in Scala. I don’t like it, I think it should be called Void instead, but it’s not something worth making a fuss about. Anyway, IntelliJ will fill it in for you if you neglect it.

To make this first test pass, it’s enough to change -1 to 9 in euclideanGCD(). For this test, it wouldn’t matter if we defined

and then used that instead of Math.abs with the same numbers, since (−27)² = 729 is a small enough number that we don’t have to worry about overflows.

Next we could do a slightly more elaborate test that pseudorandomly chooses two consecutive integers, so val expected = 1, and also uses Math.abs as the Euclidean function. I leave this one as an exercise if you’re so inclined.

Those two tests are perhaps enough to motivate us to actually write a proper euclideanGCD(). Something like this:

The more dogmatic adherents of test-driven development would probably point out that our two tests so far don’t really require euclideanGCD() to actually use eucFn().

And they’d be right. We should probably edit euclideanGCD() so that it still takes eucFn() but doesn’t use it. Then we write a test to check that euclideanGCD() does use the eucFn() we gave it in order to pass.

We do that by defining functions that are invalid by the mathematical requirements of the Euclidean GCD algorithm, but valid by the rules of Scala syntax, and pass them to euclideanGCD().

It’s perfectly possible to define exceptions in the Scala REPL. It’s only because of JUnit that I say this part is better followed along with IntelliJ.

I admit this does feel kind of silly, though. But it does help illustrate several important points about Scala.

You might notice that the Scala REPL is smart enough to notice if you define a custom exception subclassing java.lang.Exception in the REPL but it’s nothing more than a rename of java.lang.Exception.

Even though the NonEuclideanDomainException presented here does not explicitly define any new “methods,” the fact that it requires two integers and an Int to Int function to be constructed means that it actually enriches the inheritance hierarchy in a small but important way.

Back when this project was Java-only, I was going back and forth on whether the custom exceptions I was defining needed to be checked exceptions or runtime exceptions.

On the Scala side of the project, it might not matter: there are no checked exceptions in Scala. At least as long as we’re not calling from Java a Scala subroutine that might throw a checked exception, in which case we might need either an @throws annotation or make the exception subclass RuntimeException instead of Exception.

For this next sample output, I actually wrote all the source files in Windows Notepad, compiled them on the command line with scalac, packaged them with jar and then loaded them into the Scala REPL.

I mentioned earlier that f(d) being a divisor of f(n) is not mathematically required when d is a divisor of n. With Scala we now have a framework to help us explore that question.

Consider the function f(n) = 0 if n = 0, f(n) = 1 if n = 1 or −1, and f(n) = |n| + 1 for all other values of n.

By the way, in Java, don’t try to use an if statement as a summand. Or do try it to check how quickly your IDE gives you a red flag.

I suppose that in Scala it would be more proper to use a match statement for this purpose. But using the familiar if statement in this perhaps unfamiliar way helps highlight how thoroughly functional Scala is.

Now that we have alternateFunctionF() defined, we can use it in euclideanGCD().

So f(9) = 10 and f(18) = 19, and 9 is a divisor of 18, but 10 is clearly not a divisor of 19. I also tried it out with a few different pairs of numbers, just to make sure alternateFunctionF doesn’t trigger an exception in some case I overlooked.

This is of course a toy example to illustrate this nuance, which can be a lot more useful in certain other domains of numbers than it is in Z.

Trying things out in the REPL can be a very helpful complement to automated testing. You can come up with a scenario, try it out in the REPL and either confirm that your tests cover that scenario… or that you need to write a new test.

This next example demonstrates very clearly why f(n) = n² is not a good Euclidean function from a practical standpoint even though it is good from a mathematical standpoint:

Of course 46341² is not negative, but in a signed 32-bit integer, the computation overflows and gets erroneously turned into a negative integer.

Such technical issues are of some interest to me, but not as interesting as the idea of applying the Euclidean algorithm to number domains other than Z.

For example, in Z[√14], what is the function that would allow the Euclidean algorithm to resolve gcd(2, 1 + √14)? Would that function work for any pair of numbers in Z[√14]?

To investigate questions like that, I will still be using objects to represent the various number domains, and also objects to represent numbers in those domains.

And NonEuclideanDomainException will be a Java class with the instance function tryEuclideanGCDAnyway() which can only use the absolute value of the norm as the Euclidean function for its attempt.

But I will also include in the project a Scala function that allows me to experiment with various different valid and invalid Euclidean functions in the Scala REPL.

Before I get to that point, I still have a lot of testing and refactoring to do on Java objects.

Although Java itself is making moves towards functional programming, for now I’m finding it quite satisfactory for my project to leave the functional side of it to Scala.

As it turns out, JavaScript is also sort of functional. Today it’s looking a hell of a lot more functional than it did fifteen years ago, but it’s also looking a lot more object-oriented.

Given its rather haphazard development, it has been easy for JavaScript to embrace both paradigms. This reminds me of Herman Melville’s description of the penguin:

“And truly neither fish, flesh, nor fowl is the penguin; as an edible, pertaining neither to Carnival nor Lent; without exception the most ambiguous and least lovely creature yet discovered by man. Though dabbling in all three elements, and indeed possessing some rudimental claims to all, the penguin is at home in none. On land it stumps; afloat it sculls; in the air it flops.” — Herman Melville

Indeed JavaScript possesses “rudimental” claims to both the object-oriented and functional paradigms. Licensing the “Java” name is of course not a strong claim to object-oriented programming. I’ll probably catch some flak in the comments for the penguin comparison.

Photo by Sander Crombach on Unsplash

Like Kotlin, Scala can also compile to JavaScript. It might be interesting to see if JavaScript compiled from Scala really does look functional, or object-oriented, or if it looks more procedural.

A couple of weeks ago, Rainer Hahnekamp published “An introduction to Object-Oriented Programming in JavaScript” here on Medium. It was even more recently that he read Scalfani’s 2016 farewell to object-oriented programming.

Hahnekamp responded that it’s possible to misuse object-oriented programming. He’s right about that. And of course it’s also possible to misuse functional programming.

With its functional capabilities and static typing, Scala is robust, but it also allows the programmer the flexibility to choose between the object-oriented and functional paradigms according to the situation.

And by enabling functionality by making everything an object, Scala demonstrates that, for all its flaws, the object-oriented paradigm is still valid and very useful in a wide variety of situations.

is a composer and photographer from Detroit, Michigan. He has been working on a Java program to display certain mathematical diagrams.

is a composer and photographer from Detroit, Michigan. He has been working on a Java program to display certain mathematical diagrams.