If I want a serialization-safe singleton, should I prefer
case object Foo
or
object Foo extends Serializable
?
I think this depends on how you plan to use this object. Case objects are generally used with case classes to represent some kind of initial or terminal object in an algebraic data type, eg Nil or None. Regular objects usually are companions for classes to hold static methods like singleton and factory methods.
If you're planning using this object with other classes, serializing it, and maybe using it in pattern matching, defining it as a case object seems more natural to me.
Related
I have been using sealed traits and case objects to define enumerated types in Scala and I recently came across another approach to extend the Enumeration class in Scala like this below:
object CertificateStatusEnum extends Enumeration {
val Accepted, SignatureError, CertificateExpired, CertificateRevoked, NoCertificateAvailable, CertChainError, ContractCancelled = Value
}
against doing something like this:
sealed trait CertificateStatus
object CertificateStatus extends {
case object Accepted extends CertificateStatus
case object SignatureError extends CertificateStatus
case object CertificateExpired extends CertificateStatus
case object CertificateRevoked extends CertificateStatus
case object NoCertificateAvailable extends CertificateStatus
case object CertChainError extends CertificateStatus
case object ContractCancelled extends CertificateStatus
}
What is considered a good approach?
They both get the job done for simple purposes, but in terms of best practice, the use of sealed traits + case objects is more flexible.
The story behind is that since Scala came with everything Java had, so Java had enumerations and Scala had to put them there for interoperability reasons. But Scala does not need them, because it supports ADTs (algebraic data types) so it can generate enumeration in a functional way like the one you just saw.
You'll encounter certain limitations with the normal Enumeration class:
the inability of the compiler to detect pattern matches exhaustively
it's actually harder to extend the elements to hold more data besides the String name and the Int id, because Value is final.
at runtime, all enums have the same type because of type erasure, so limited type level programming - for example, you can't have overloaded methods.
when you did object CertificateStatusEnum extends Enumeration your enumerations will not be defined as CertificateStatusEnum type, but as CertificateStatusEnum.Value - so you have to use some type aliases to fix that. The problem with this is the type of your companion will still be CertificateStatusEnum.Value.type so you'll end up doing multiple aliases to fix that, and have a rather confusing enumeration.
On the other hand, the algebraic data type comes as a type-safe alternative where you specify the shape of each element and to encode the enumeration you just need sum types which are expressed exactly using sealed traits (or abstract classes) and case objects.
These solve the limitations of the Enumeration class, but you'll encounter some other (minor) drawbacks, though these are not that limiting:
case objects won't have a default order - so if you need one, you'll have to add your id as an attribute in the sealed trait and provide an ordering method.
a somewhat problematic issue is that even though case objects are serializable, if you need to deserialize your enumeration, there is no easy way to deserialize a case object from its enumeration name. You will most probably need to write a custom deserializer.
you can't iterate over them by default as you could using Enumeration. But it's not a very common use case. Nevertheless, it can be easily achieved, e.g. :
object CertificateStatus extends {
val values: Seq[CertificateStatus] = Seq(
Accepted,
SignatureError,
CertificateExpired,
CertificateRevoked,
NoCertificateAvailable,
CertChainError,
ContractCancelled
)
// rest of the code
}
In practice, there's nothing that you can do with Enumeration that you can't do with sealed trait + case objects. So the former went out of people's preferences, in favor of the latter.
This comparison only concerns Scala 2.
In Scala 3, they unified ADTs and their generalized versions (GADTs) with enums under a new powerful syntax, effectively giving you everything you need. So you'll have every reason to use them. As Gael mentioned, they became first-class entities.
It depends on what you want from enum.
In the first case, you implicitly have an order on items (accessed by id property). Reordering has consequences.
I'd prefer 'case object', in some cases enum item could have extra info in the constructor (like, Color with RGB, not just name).
Also, I'd recommend https://index.scala-lang.org/mrvisser/sealerate or similar libraries. That allows iterating over all elements.
I have read how companion and singleton objects can be used to keep static methods, which makes sense. My question is how is this object made or instantiated it? I have read from some sources on how Objects are instances of the class if used as companion objects while others say they are not instances of the class. Then how are the objects existing or being made? Plus the Object would be same class data type I suppose?
My question is how is this object made or instantiated it?
[…]
Then how are the objects existing or being made?
You don't know, shouldn't know, and can't know.
The Scala Language Specification says that they exist. The Scala Language Specification does not say how they are instantiated. Every implementor is free to implement them however they want.
For example, ECMAScript has object literals, so for Scala.js, there is no need for them to be an instance of any class at all. Scala-native is not dependent on any platform, so it can do whatever it wants. Scala-JVM needs to comply with the restrictions of the JVM, where every object needs to be an instance of a class. In Scala-JVM, every singleton object Foo is an instance of a JVM class named Foo$.
Plus the Object would be same class data type I suppose?
The type of a singleton object Foo is the singleton type Foo.type. It's not a class.
I have read from some sources on how Objects are instances of the class if used as companion objects while others say they are not instances of the class.
Instead of reading "some sources", it's much better to just read the source: the Scala Language Specification, specifically section 5.3.2 Case Classes:
A case class definition of 𝑐[tps](ps1)…(ps𝑛) with type parameters tps and value parameters ps implies the definition of a companion object, which serves as an extractor object. It has the following shape:
object 𝑐 {
def apply[tps](ps1)…(ps𝑛): 𝑐[tps] = new 𝑐[Ts](xs1)…(xs𝑛)
def unapply[tps](𝑥: 𝑐[tps]) =
if (x eq null) scala.None
else scala.Some(𝑥.xs11,…,𝑥.xs1𝑘)
}
Each object has its own class, but you can't access the class directly. This class has a constructor without parameters which is called automatically when it's loaded and creates the only instance.
Objects are instances of the class if used as companion objects
Either you misunderstood or you really shouldn't trust these sources. It's possible for a companion object to extend the trait/class it's companion to, but not at all common.
Companion objects are not instances of the class they're companion of, think of them more like a collection of utility methods. If you're familiar with Java - all the method, that you made static in Java (hence they don't belong to a particular instance, but to class in general) would go to Companion object in Scala. Also, companion objects have access to classes private values.
Objects are lazily initialized for you, you don't need to know when and how exactly are they created, just if you call a function from an object - it will be created for you, and there will be only one instance of it.
I understand that using something like
case class private A()
new A()#This will be a invalid call as A is private
But what I do not understand that as from an implementation perspective, what advantage does this provide while coding? Because calling A() twice will give 2 instances of the class anyways. If this syntax is not used to prevent instantiation like Java, then why would I want to not let someone instantiate my class using new?
Marking a case class constructor private is useless. As you've notices, case classes get a synthetic companion object with an apply method whose implementation is simply a call to the actual constructor.
Scala case classes have been designed to just "classes + the case modifier", meaning that everything that works on classes also works on case classes, which also include the (pointless) ability to specify access modifiers on the constructor.
I am learning Scala and Akka and in my recent lookup for a solution, I found something like
case class TotalTaxResult(taxAmount:Double)
case object TaxCalculationTimeout
What is the difference between the two?
When should I use one over the other?
A case class can take arguments, so each instance of that case class can be different based on the values of it's arguments. A case object on the other hand does not take args in the constructor, so there can only be one instance of it (a singleton, like a regular scala object is).
If your message to your actor does not need any value differentiation, use a case object. For instance, if you had an actor that did some work, and you, from the outside, wanted to tell it to do work, then maybe you'd do something like this:
case object DoWork
...
def receive = {
case DoWork =>
//do some work here
}
But if you wanted some variation in how the work is done, you might need to redefine your message like so:
case class DoWorkAfter(waitTime:Long)
...
def receive = {
case class DoWorkAfter(time) =>
context.system.scheduler.scheduleOnce(time.milliseconds, self, DoWork)
case DoWork =>
//do some work here
}
A case object is a singleton case class. They are used kind of like enumeration values. It can be used in pattern matching just like any other value:
TaxCalculationTimeout match {
case TaxCalculationTimeout => println("hello")
}
When you define a case class, you are creating a template for instances of that class. TotalTaxResult(1.0) and TotalTaxResult(2.0) are two different values of the same type. Whereas there is exactly one TaxCalculationTimeout value.
In simple words, Scala is a Object Oriented and Functional programming language. It have features of functional programming like pattern matching with pure object oriented methodology.
Some times, we need to create singleton object without any value like passing some signal for pattern matching. If scala have not concept of case object we just to need to use enum or equals some string value in matching. But this is not a readability in pure Object Oriented language.. In that scenario we are using Case Object
Case classes are used when we need to create multiple objects with different values.
You can think of these two to be just like a class and an object in general.
When you do a case class ClassName(params list) it creates a blueprint for making objects and case object defines a singleton object in the scope in which it is declared.
Starting with Scala 2.10, you should always use case objects instead of case classes with no arguments.
So when you want to do a pattern match on some values which need arguments you should go for a case class, but if your values don't take arguments then you should use a case object.
I'm defining a Scala class today, and I think "I need an equals method and a hashCode method; and a copy method would be handy too. I'll turn this into a case class." My class already has a bunch of other code, and is in no way trivial.
So fine, it all works and everything, but when the text books deal with case classes, all of the examples define them for use as value classes or 'data transfer objects'. Is it appropriate to define a non-trivial case class? Is the thought process described above OK, or do I need to think of case classes differently?
A case class provides, equals, hashCode and toString methods based on the main constructor parameters, all of which are turned into val too. In addition, the object companion gets an apply and an unapply methods, again based on the main constructor parameters.
Also, a case class inherits from Serializable and from Product, and should not be extended by other classes.
If all of these things are appropriate for your class, then feel free to declare it as a `case class'.
Feel free, provided it doesn't have descendants. Extending case classes is a bad idea.