Scala Saturday: Functions for the Object-Oriented

Functions as objects with a nicer syntax

When learning Scala, I had a tendency to think that everything related to functional programming was somehow magical and different. This kind of thinking impeded my ability to understand how functional constructs work and relate to my object-oriented background. Today we’re going to learn about functions in a world of objects. If you know your way around objects, you can understand functions just as easily.

Traditional functions

Most programming languages have the concept of a function. A function is a named, reusable block of code that may take parameters and may return a value.

Have you met my ex?

You use (or “invoke” or “apply”) a function by spelling out its name, using parentheses, and providing values for its arguments. A function will often live in some scope or be attached to a top level object as methods. (In this case, the isEven and repeatX functions live in some implicitly defined scope.)

Functions are important for abstraction and reusability. With the isEven , we’re saying we want to know the parity of not just a specific number but any number we encounter. You’ll see in a bit that functional programming takes this idea into high gear by abstracting over not only values but entire types and behaviors.

Flying first class

A first-class function can be passed around like a value.

Simply having support for functions does not make a language functional. In some languages, functions have a lot more mobility than staying attached to a class. Consider the following code.

Big numbers. Huge.

The first two functions double and square are traditional functions. They abstract over specific double and square values by requiring an input n .

Compare the printBigger function which has two parameters. The n parameter makes it seem like a traditional function in that we are able to print for any value of n . The second parameter however is much more powerful. We know printBigger will print a larger version of its first argument, but we don’t know exactly how that number will be made bigger. That behavior is left to the function f that is passed in as an argument, taking in any function that transforms numbers. The ability to pass around function as values is one behavior that distinguishes functional languages from non-functional ones. This property is often referred to as having support for “first-class functions”.

Passing around functions also allows us to separate concerns more easily and more often. The function argument enables printBigger to focus solely on printing while having its number-changing behavior provided or “dependency injected”. We’re left with individual functional parts that have a narrow focus and are thus easier to test and reason about.

The oops in loops

Less code is better code.

My ex is back.

We’ve all written code like this. Counters and buffers need to be initialized, counters are tested, things happen, buffers grow, and then counters are incremented. Rinse, repeat. It’s very boring stuff! We only care about two things in the paragraph above. One is checking if the numbers are even and two is transforming the number into a string. The rest is boilerplate.

Yuck, boilerplate. Even if we copy/paste loops from old code, we still have to make modifications to the parts that are doing “the stuff”. This is an extremely error prone process, especially if the loops are complex or nested. Additionally, there’s something suspicious and non-generic about using the same blocks of code over and over again. Could it be abstracted?

Often times one’s first exposure to functional programming in multi-paradigm languages (like JavaScript, Python, now Java) is learning how to rewrite loops.

Tight.

This code is written in a more functional style. The filter and map methods come from Scala’s collections framework. They take functions as arguments and handle all of the counting, incrementing, and buffer manipulation behind the scenes. It’s much more clear now that our focus is on filtering and transforming. With less code to fiddle with there’s also less opportunity for making mistakes.

Good functional code will look surprisingly terse when compared to equivalent object-oriented code. As you write more functional code in a mixed-paradigm language like Scala, you’ll see your code style evolve. You’ll learn that less code is better code.

Objects need not apply

Every function value is an instance of a class.

We’ve established that functions are like values that you can assign and pass around. But are they still special in some way? How do they compare to other values like objects? If we were to view functions as instances of classes, what structure would their classes have?

Feeling redundant.

Here are two of our functions from before and plus one more. If you step back, you’ll notice a pattern. Each class has a symmetric structure. There’s one method on each that has an awfully redundant name, takes in one argument, “does the stuff”, and returns something. If we were to factor this idea out into an abstract class or trait, what would that look like?

To apply or not to apply.

Now all of the functions extend from the same generic interface MyFunction (plus some fancy type parameters). We’ve structurally encoded that these behaviors are all functions, but they’ve had their individual method names stripped away. We could have renamed them “doStuff” but went with apply instead and gained something in exchange. Notice how the function calls to repeat and twiceLength look different than the one to square ? square is calling its apply method explicitly while the other two now resemble traditional function calls even though they are function values and not functions. In Scala, the method name apply is optional and can be left off when calling it. This syntactic sugar helps give Scala its expressive power and allows you to use user-defined mechanisms in a much more palatable way.

Functions doing object things.

At this point, maybe you’re like me and thinking… “If functions are just objects and functional programming is about functions, does that mean every object-oriented language can be functional?” Yes and no. You could encode every functional pattern using objects the same way you could encode objects using assembly. The result wouldn’t look or feel very functional or fun. Being functional is more like a spectrum or gradient. What makes a language functional are the tools that are easy to use or reach for. Functional languages like all higher level languages are a matter of usability. Is it easy for me to express myself in a functional way using functional primitives?

Scala can even do you one better. Because we don’t like boilerplate and because we want functions to be easy to reach for, we can rewrite the classes using an inline, anonymous syntax.

Here’s an example that demonstrates three different styles. The first style is a traditional method, but we’re able to treat it like a function in the map . The second style is defining an anonymous function object but assigning it to a named value for easy reuse. The third style is defining an anonymous function inline to map .

To make your code more self-documenting and easier to test, I would recommend creating named methods for your functions. That’s the convention my colleagues and I have at Tapad when using Scalding, a Scala framework for processing big data on Hadoop that is syntactically similar to the Scala collections framework. We found it difficult to understand and maintain our code if everything was inlined. Sometimes the bodies of these functions would be quite large and complex. But the inline syntax can be handy for much shorter snippets and logic that fits comfortably on one line.

F rom A to B

From rags to riches and from boilerplate to functions. Can you believe there’s still some boilerplate left? Let’s look at the type annotations for some functions.

square: MyFunction[Int, Int] — from Int to Int

— from to isEven: MyFunction[Int, Boolean] — from Int to Boolean

— from to twiceLength: MyFunction[String, Int] — from String to Int

Functional programmers refer to functions by their input and output types so often that there is a shorter, dedicated syntax. In Scala, this arrow is just syntactic sugar for Scala’s core Function traits.

square: Int => Int

isEven: Int => Boolean

twiceLength: String => Int

You’ll see this a lot.

Using this syntactic sugar, we care less about the function’s class as a vehicle for its implementation and more on the nature of the function as a transformation. From Int to what? From something to Boolean , etc.

You’ll sometimes hear functions being compared to building blocks. Ever play with Legos? These plastic pieces have many combinations that fit together but only in specific arrangements. The blocks have to align and snap together. Functions are similar in that they can combine with other functions but only in specific ways. We have Scala’s type system helping us make sure we’re only combining functions that make sense. In this case, we could combine square and isEven by calling one after the other.

Next time you find yourself writing data transformations in a loop consider that you might be doing functional programming in disguise! Maybe you’re using a mixed-paradigm language that supports functional ideas on the small scale. Keep an especially close eye on better ways to write loops. What you’ll find is most likely functional.