Let’s say a case class models a game state. And whether a game state is won/lost can be computed by a method, from some fields on a given state instance. This method to compute win/loss can be either defined inside the case class itself, or outside it, eg in its companion object. This changes the api experience correspondingly,
stateInstance.isWon in the former case (concise) and
State.isWon(stateInstance) in the latter (verbose).
What implications does this have on app’s performance/memory-footprint?
If the method is defined inside the case class, does instantiation of multiple case classes imply redundant memory footprint because, the methods defined inside, occupy their space as well, in all the case class instances? Or, on the contrary, does the compiler eliminate this duplication?
Or, if the method is defined on the companion object, which is a singleton, does this mean that the aforementioned redundancy is eliminated, because those methods are present only in one single space in the app’s runtime?
First of all, as an advice, you really shouldn’t bother about performance optimizations until you have used proper benchmarking to determine bottlenecks and the impact of such optimizations.
Second, methods exists in one place, at runtime a lookup table is used to invoke the correct one.
So, IMHO, just go with the one that looks better for you and your users. And is easier to read, understand and rector.
The definitions of methods of a type A do not live inside any A instance, but in a separate class instance of type Class[A], which you can obtain via classOf[A] of A.getClass. Having multiple instances of type A will not multiple the method definitions.
Are you referring to run-time or developer efficiency?
If run-time, keep in mind the maxim: Premature optimization is the root of all evil. First make the code work correctly, then work to make if faster, if really necessary. Profile before optimize.
In terms of developer efficiency, we start splitting hairs over 𝘴𝘵𝘺𝘭𝘦 of code: object oriented, functional, a combination of both, …
Though no expert by any stretch, I’ve done some work with Clojure, which, as a Lisp with default immutable data structures, basically forces the functional approach. It seems more natural to work bottom up, staying with smaller functions. Hand-in-glove with the KISS principle.
If a project team or individual developers are dedicated to keeping to the functional model, then majority of functions are going to wind up in an object, whether it be a standalone object used to logically group function or in companion objects.
Object oriented and functional are not necessarily two opposites. Instance methods don’t make code less functional IMHO.
That is true. Martin Odersky said that FP and OO are orthogonal concepts. A class can be perfectly functional if it contains no mutable fields and the methods have no side effects.
Like said by others - premature optimization is the root of all evil. In my experience such minute considerations were never a problem performance wise.
However, in terms of design, I would suggest placing that evaluation function someplace else entirely. If it’s business logic, and not just a pure translation of the state (like
isEmpty on collections), then it shouldn’t reside in a case class, and most certainly not in any instance object.
Business logic should be expressed in a non-global / non-static unit, and should have its own tests.