Experiment 09

19 minute read

This post explores the possibility of using inclusive programming languages, i.e. ones that successfully accommodate functional and object-oriented programming styles in a single codebase.

Introduction

Python, C++, Java, and C# are 4 out of the top 10 most popular languages on any survey/poll/list, and they have one thing in common - they are all primarily object-oriented languages. This paradigm is characterized by1:

  • Data/operations are encapsulated in objects
  • Information hiding is used to protect internal properties of an object
  • Objects interact by means of message passing
  • Classes are organized in inheritance hierarchies

But even these classic languages are now including new features to support a more functional programming style; some people see this as a sign of an upcoming paradigm shift2. My thoughts are more in line with Richard Feldman who argued in his talk “Why isn’t functional programming the norm?” that we are currently undergoing an intermediate phase where we are starting to apply functional programming techniques within object-oriented languages. This post uses 5 criteria to find suitable languages to be used for this purpose.

Inclusive programming criteria

The ideal inclusive language is one that allows each individual within a team to follow their preferred programming style (i.e. object-oriented or functional). Alas, no such language exists. We next examine the critical elements necessary to provide successful collaboration between these unique programming styles. These elements are used to support and identify each paradigm.

This concept is distinct from that of multiparadigm languages. The primary difference being that while multiparadigm languages might support more than one paradigm, they may not necessarily be well-suited to support more than one paradigm within a single codebase.

Criteria to support an object-oriented paradigm

I identify 3 properties that are essential for any object-oriented programmer to feel at home in a language:

O1. classes with methods and properties

O2. encapsulation (i.e. the ability to hide data/methods)

O3. subtype polymorphism (subtyping)

Encapsulation and subtyping3 are necessary to apply common design patterns; they also allow programmers to adhere to guiding principles that are well-known within their community. For example, subtyping enables both the “O” and “L” in SOLID design principles.

This relatively minimal subset of language features doesn’t narrow our options much yet; let’s discuss the elements critical to functional programming.

Criteria to support a functional paradigm

The two most defining features of the functional programming style are pure functions4 5and immutable data. But these strategies do not require any special language features. One criteria that is often needed to support functional programming is that functions are treated as first-class citizens6.

F1. functions are first-class

Most modern languages treat functions as first-class citizens.

Criteria to identify each paradigm

To properly meet our definition of inclusive we must also be able to identify which programming paradigm a particular piece of code follows.

How do you identify object-oriented code? This paradigm is full of easy to spot code smells:

  • classes
  • for loops
  • mutable variables

How do you identify functional code? At a glance it may be difficult to pin down functional code, but here are a few things to look for:

  • no classes
  • no for loops
  • no mutable variables
  • lots of functions

Classes and for loops are easy enough to spot, but what about mutable variables? In order to facilitate this distinction, we add the following criteria:

C1. mutable variables must be designated as such

This final criteria drastically narrows down the field. The remaining7 contenders that make our list of inclusive languages are: Kotlin, Scala, Rust, Swift. Let’s briefly examine how each language meets the criteria.

Inclusive languages

This section is included to show how each of the languages meet the inclusive criteria. You may choose to skip it.

Kotlin

Kotlin is a “modern programming language that makes developers happier.”

  • O1 - has regular classes (plus enums, data classes, and sealed classes)
  • O2 - class properties/methods can be made private
  • O3 - uses interfaces for creating subtypes
  • F1 - functions are first-class (also supports anonymous functions)
  • C1 - immutable variables are initialized with val; mutable variables are initialized with var

Scala

Scala combines object-oriented and functional programming in one concise, high-level language.”

  • O1 - has regular classes (plus enums and case classes)
  • O2 - class properties/methods can be made private
  • O3 - uses interfaces for creating subtypes (with mixin composition to compose components)
  • F1 - functions are first-class (also supports anonymous functions)
  • C1 - immutable variables are initialized with val; mutable variables are initialized with var

Rust

Rust is a “language empowering everyone to build reliable and efficient software.”

  • O1 - has structs with implementation methods
  • O2 - everything is private by default; pub makes it public
  • O3 - uses traits for creating subtypes
  • F1 - functions are first-class (also supports anonymous functions)
  • C1 - variables are immutable by default; mutable variables are designated by mut

Swift

Swift is a “general-purpose programming language built using a modern approach to safety, performance, and software design patterns.”

  • O1 - has regular classes (also enums and structs)
  • O2 - class properties/methods can be made private
  • O3 - uses protocols for creating subtypes
  • F1 - functions are first-class (also supports anonymous functions)
  • C1 - immutable variables are initialized with let; mutable variables are initialized with var

Why should your team pick an inclusive language?

Successful software development requires solving two kinds of problems - technical and human.

I believe inclusive languages offer benefits on both of these fronts. On the technical side, neither style is universally superior. The object-oriented approach is usually better if your system operates on things and new features typically involve adding new things (rather than new operations). On the other hand, if new features typically require new operations then a functional style may be a better fit8.

They can also be used in conjunction; clean architecture may be a good model. Following this approach, you can model your domain and write all of your core business logic using functional code and follow an object-oriented style at the adapter and interface layers.

I believe that providing your teammates with the ability to code as they want to, using the paradigm of their choice, will increase team happiness and morale. I hope it also encourages conversations and teaching moments from both sides. Will you be more productive? Will you create a better design? Will you get a better product? I don’t know. I haven’t tried this experiment myself.

Next steps

Which language should I pick? Here are a few questions to help you make a decision.

  • Are you doing systems programming? -> Rust
  • Are you working on iOS? -> Swift
  • Are you working on Android? -> Kotlin
  • Do you need to interoperate with other Java code? -> Scala/Kotlin

If you don’t identify strongly with any of the questions above, you may want to consider a language that is easier to learn (especially if you are teaching an entire team). Based on the collective background of the team, here are my recommendations:

If you are coming from a Java background…

kotlin < scala < swift < rust

If you are coming from an Objective-C background…

swift < kotlin < rust < scala

Still unsure? Ok, Ok, don’t twist my arm. Pick Kotlin :)

Conclusion

You can write crap code in any paradigm. - Dave Farley

There is clear division between those who use more mainstrain (i.e. object-oriented) languages and those who use functional languages. It is a “them vs. us” mentality, but I believe there is a middle ground where these two crowds can meet, learn, develop, and succeed together. And I believe this middle ground is an inclusive programming language.

Thanks for reading!

Footnotes

  1. Object-oriented paradigm 

  2. “Why Functional Programming Should Be the Future of Software Development,” IEEE Spectrum, Oct. 23, 2022. [Online]. Available: https://spectrum.ieee.org/functional-programming. [Accessed: Nov. 14, 2022] 

  3. Subtyping is also known as interface inheritance, whereas subclassing is known as implementation inheritance or code inheritance (see Liskov substitution principle). 

  4. A pure function is a function that, given the same input, will always return the same output and does not have any observable side effect (from Professor Frisby’s Mostly Adequate Guide to Functional Programming

  5. A side effect is a change of system state or observable interaction with the outside world that occurs during the calculation of a result. Examples include: reading a file, inserting a record into a database, making an http call, printing to the screen, or getting user input. 

  6. This means the language supports passing functions as arguments to other functions, returning them as the values from other functions, and assigning them to variables or storing them in data structures 

  7. Several languages just missed the cut. C#, C++, Java, and Go meet all the conditions except that mutable variables are not designated. F# and OCaml actually met all the criteria, but I do not believe that the average object-oriented programmer would be comfortable using them. 

  8. Philip Wadler named this the expression problem. Another good reference here