Scala enables immutability and functional programming but it does not dictate it. As easy it is to create immutable state via a val
it is equally easy to create mutable state via a var
. IMHO this is a good thing since the programmer is free to choose the programming style to use according to the problem at hand.
One of Scala’s unique features is singleton objects. Singleton objects are an implementation of the Singleton design pattern inside the language and they allow Scala to abandon static methods and provide a more uniform view of the world where everything is an object.
Singleton objects despite all their benefits they make one bad practice very convenient to do. It is very easy to forget that an object
(that is not defined inside a class
or trait
) is actually a global static and thus a var
inside that object
is actually global mutable state.
This is not a Scala weakness and I believe it is more a weakness of the programmer to come up with the right design given his unfamiliarity with the Scala constructs. When we start using a new programming language because we are re-evaluating our view of programming certain bad practices that we might never used in our previous language temporarily become more tempting.
One example of global mutable state in a top-level singleton object that I saw recently is in Squeryl. Squeryl is an ORM that has a really slick API and IMHO could become a viable alternative to the major Java ORMs for Scala.
I was reading Squeryl’s documentation and I got really excited about using it in a small project but then I saw the SessionFactory object and my excitement turned into disappointment. Below I have a small fragment from the SessionFactory
‘s code:
object SessionFactory {
var concreteFactory: Option[()=>Session] = None
var externalTransactionManagementAdapter: Option[()=>Session] = None
def newSession: Session = {
/* Code that uses concreteFactory... */
}
}
As you can see from the above code the SessionFactory
has two var
s that hold functions that create ORM sessions. I have to say that I am at least uncomfortable with an ORM that relies on global mutable state for that functionality.
Lets try to enumerate some of the disadvantages of Squeryl’s current design:
- Since the
concreteFactory
andexternalTransactionManagementAdapter
can be changed from anywhere in the program it is difficult to reason about our code. - The API of
SessionFactory
is not thread-safe. For example if a thread changesconcreteFactory
then another thread reading that variable does not have a guarantee that will see the latest version. - Since the configuration for creating a
Session
is in a static variable this means that we cannot have a multi-threaded application that uses two different configurations. - As a consequence of the above we cannot deploy Squeryl as an OSGi bundle and use it from multiple applications.
- Since we depend on global state, our code is more difficult to test because we need to reset that state after every test.
- Furthermore we cannot run our tests in parallel.
Lots of bad stuff for two innocent var
s…
Update: Maxime Lévesque, the author of Squeryl, wrote in a comment that Squeryl also provides a mechanism for working with sessions that does not rely on static state and that the mechanism described in this article is optional and provided for convenience. So my analysis above is not true and would only be valid if Squeryl didn’t provide an alternative mechanism.
In conclusion:
- Putting a
var
inside anobject
might be convenient but as each instance of global mutable state it can lead to a lot of trouble. - Just because you moved to a new programming language doesn’t mean that your old practices are now unnecessary.
I would like to mention that the the two vars that you have spotted are part of
a bootstraping mechanism who’s only purpose is to allow this syntax :
transaction {
… your squeryl code here ….
}
(in addition to allowing session creation code supplied by user code
of course …it’s an abstract factory)
This is a bootstrapping mechanism, i.e. the kind of code that you set right after having read
the configuration, most likely at VM start time.
This mechanism is actually optionnal, you can use this alternative syntax :
val session = new Session(…)
using(session) {
…your Squeryl code here …
}
All the issues you’ve mentioned won’t apply if you use this syntax,
because you won’t be using the statically overridable factory.
Scala is an “impure” functional language, i.e. immutability is strongly recommented
but not enforced, the rational is that there will be a few cases where
mutability is just more practical. Of course there must be a justification for introducing
the mutability. My justification in this case is that in the predominant use cases
need only one Session factory per VM, in these cases, there is just no point in setting it
more than once, if someone needs to do this, they should be using sessions explicitely
i.e. the using(session) {} syntax.
You will notice that practically all Squeryl objects (Query[A], Table[A], Schema, etc…)
are immutable, in fact they are meant to be shareable.
I didn’t knew that the mechanism with the static factory was optional. I really like Squeryl and I was really disappointed when I saw the vars inside SessionFactory. I should have kept reading the docs 🙁
I believe that we both agree that the mechanism with the static factories has some serious drawbacks but it can be used in smaller apps as a convenience.
Thanks for letting me know! I’ll update the article and I’ll probably give Squeryl a try really soon 🙂