I’ve been using Scala at work lately and one of its strangest features has been the ability to create singletons with the “object” keyword. This is considered a “beginner” feature but it I’m struggling to feel totally comfortable with it. I think the reason that it feels so unnatural is that this single feature packs in several unrelated concepts. Let’s try to get some clarity on this by breaking this feature down a bit.
So what does the “object” keyword even do? Let’s look at a simple example:
This code has the following effects: It creates an underlying type for the singleton called MyObject.type (which contains SomeField and SomeMethod). And it makes the value MyObject available in the current package. Objects are not instantiated until they are used, so the following is okay:
But this would cause your program to crash:
Since this is just a language-supported implementation of the singleton pattern, all the benefits that singletons have over static methods and classes also hold here. You can pass around variables that point to the singleton and you can use it as the argument and return type of a method. (This is also where the object.type underlying type comes into play.) You can also inherit from base classes and implement traits as you would a normal class, though extending objects is not allowed.
Objects can also be “companions” to classes. That is, if an object and class with the same name are defined in the same source file then they are companions to each other. The object is the “companion object” of a class and the class is the “companion class” of the object.
The companion object can be used as a place where you can put functionality that’s related to a class but isn’t directly tied to any particular instance. Things you would make static in other languages would go here, as would things like factories.
But this is more than just a coding convention, companion objects and classes actually have access to each other’s private fields:
So all this above explains the what of this feature, but what about the why?
The more I look at it the more it feels not-quite-right. I haven’t used Scala that much so it could be that this is something that will grow on me, but I don’t think the issue is unfamiliarity.
As far as I can tell things turned out this way because of two competing goals the language designers set for themselves:
- To be a pure object oriented language.
- To be compatible with Java.
By pure OO, I don’t mean only OO, of course blending OO and functional programming is like the whole point of Scala. This quote from Programming in Scala (2nd ed, pg 56), which is authored by Scala’s creators, explains it:
For instance, many languages admit values that are not objects, such as the primitive values in Java. Or they allow static fields and methods that are not members of any object. These deviations from the pure idea of object-oriented programming look quite harmless at first, but they have an annoying tendency to complicate things and limit scalability.
By contrast, Scala is an object-oriented language in pure form: every value is an object and every operation is a method call.
I can appreciate the thinking behind that but the problem is that Java does have static members. So you get into this situation where you have to take your pure building blocks and build something that imitates the impure. (Kind of like how pure FP languages have to imitate side-effects.)
So with these competing goals they eventually landed on this singleton / companion object solution. And I’m sure lots of thought went into it and that the people behind Scala are all brilliant but as an end user of the language this feature just feels off to me. More specifically:
- Singletons as a language feature – Cool, makes perfect sense.
- Companion objects to store misc. functionality – It’s okay but could potentially go against single-responsibility principle.
- Companions having access to each other’s private data – Seems to be breaking encapsulation. If they’re in the same file, why should they separate things in the first place?
So it’s not a complete disaster or anything, it’s just kind of imperfect. IMO allowing static into the language and giving up on purity would have been the better way to go, but hey, I’m sitting here struggling with “beginner” features, so what do I know?