Enforce values a class member can be assigned : Scala - scala

I intend to restrict the values the string member of my class can take. For eg: for a member named country, only allowable values should be the countries that i have defined like 'US', UK, CN, etc. This constants need to be string constants.
I thought of using Enum, but that does not give me flexibility with the String constants. Or may be i am not aware of how to use it.
Any suggestions?

What is the problem with the enum? Seems like a good fit for what you need. Here is an example:
object Country extends Enumeration {
val US = Value("United States")
val UK = Value("United Kingdom")
}
case class MemberCountry(country: Country.Value)
//example instantiation
MemberCountry(Country.UK)
MemberCountry(Country.withName("United States"))

Enum support is not that good in Scala. First, scala's built-in Enumeration is crap, don't use it.
The common approach for enumerations is sealed trait/class:
sealed abstract class Country(val name:String)
object Country {
case object US extends Country("US")
case object UK extends Country("UK")
case object CN extends Country("CN")
val values = Set(US, UK, CN)
}
Country.values.find(_.name == "US")
It has one problem, if you want the list of all possible values, you need to enumerate them yourself (the values field in the example above).
However, there are alternatives that solve that problem.
You can go with macro (check this thread).
Or use third-party library.

Enumeration has its strengths and weaknesses in Scala.
You could use any approach highlighted above namely:
Enumeration subclass
Use Sealed Trait/Class.
Both approaches have benefits and potential tradeoffs. It might be worth reading through this informative article on this issue:
http://underscore.io/blog/posts/2014/09/03/enumerations.html

Related

Using Enumerations in Scala Best Practices

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.

Is there a difference between extending a trait with a type or using a type parameter in your case class?

I recently had a coworker implement a trait like
trait CaseClassStuff{
type T
val value: T
}
and then used it to instantiate case classes as
case class MyCaseClassString(value: String) extends CaseClassStuff { type T = String }
case class MyCaseClassDouble(value: Double) extends CaseClassStuff { type T = Double }
and I thought that was particularly whacky since it seemed reasonable enough to just do
case class MyCaseClass[T](value: T)
to get the exact same result. There was argument over how using the trait allowed us to avoid needing to update anything using that case class, since with the trait we just explicitly used MyCaseClassString and MyCaseClassDouble in different areas, but I wasn't sure how since they seemed to be ostensibly the same thing, especially since the only change between the two is their type. The program using them was set up to parse out the logic when it was a double or a string received.
So, my question is about whether or not they are different as far as the compiler is concerned, and whether or not there is actual benefit from doing it the way with the trait in general, or if it was just specific to my situation. It wasn't clear to either of us if it was best practice to use the trait or just the type parameter, since it seems like two ways to accomplish the same outcome.

Why I need that field like that

case class AlertWindowDto(id: String)
protected val InitialWindowPeriodOneOnPeak = AlertWindowDto(ValidId)
protected val ValidId = "someSite"
I saw these there lines in different different classes. just I put together for understanding.
In general, If i am creating an dummy or some object of Class, then I give some value or null or empty string. What is the need of creating another field ValidId and assign some value and assign that field to final object.
is there any benefit, or anything help in test cases.
could you please help me.
Imagine this:
protected val InitialWindowPeriodOneOnPeak = AlertWindowDto("someSite")
Does it convey the information that "someSite" is a valid id for an alert window?
This is a trivial example, but the general idea is that sometimes breaking down expressions and assigning names to them is great for expressing meaning.
I would also add that the more this naming information is in the types, the better. For instance, here's another way of achieving the same result, without using a variable name.
case class ValidId(value: String) extends AnyVal
case class AlertWindowDto(id: ValidId)
protected val InitialWindowPeriodOneOnPeak = AlertWindowDto(ValidId("someSite"))
Same information, but the "valid id" information is now stored in the type system.

Make a trait require an unspecified Enumeration as part of its interface

How can I define a trait which defines that my case class should contain an Enumeration of some kind, when I don't want it locked to one specific Enumeration? See the explanation for more details:
I have some data that has three integer fields per entry, and each integer increases the specificity of the data.
The first field is the overall type of the data, like what industry is it from (e.g. baking), the second field is what kind of product it is (e.g. cake), and the third field is the type of product (e.g. strawberry pie).
I'm trying to map this data like so:
Each industry has its own case class
case class BakingProduct(...)
Each case class has an Enumeration in it.
case object BakingType extends Enumeration { type Product = Value, ... }
case class BakingProduct(primary: BakingType.Product, ...)
Each case class also holds an Int for the final field (it's not important enough for me to make an Enumeration for it)
case class BakingProduct(primary: BakingType.Product, secondary: Int)
To me this strikes a good balance between readability and writability, as opposed to creating case classes for every single combination, but my issue is that I want to create a trait that can encompass these Product classes; something like this:
trait ProductType {
val primary: Enumeration,
val secondary: Int
}
such that
case class BakingProduct(primary: BakingType.Product, secondary: Int) extends ProductType
is valid, but without locking primary to a specific type of Enumeration, such that I can make an Enumeration for each specific ProductType I want to use.
Should I try to make a parent Enumeration to encompass those other Enumerations (and if so, how? I haven't been able to figure that out)? I would also like to avoid making the trait generic if possible, so I don't have to give type parameters whenever I'm dealing with one. But if there's a nice way to do it with generics then that would be welcome as well, along with other suggestions :)

Scala value object hierarchies

I'm new to Scala and trying to understand how I should be modeling these objects.
The goal here is to have an object that will be stored into a database. The data to store will come from a POST. The post does not contain all of the data that will be persisted.
The OO side of me says to make a base class with the common fields. Extend it represent the data that is posted, and extend that to represent the object that is persisted. However, it seems that case classes are used for this sort of thing, and case class inheritance is discouraged/deprecated/buggy, so I'm not quite sure what I should be doing.
Also, the repetition feels very... wrong.
I am hoping someone a bit more experienced can offer some insight on how to approach this.
abstract class TestBase(val someField: String)
case class TestPost(override val someField: String) extends TestBase(someField)
case class Test(testId: String, override val someField: String) extends TestBase(someField)
Also, if I did continue with this approach, how would you copy fields from a TestPost instance to a Test instance?
although you are doing OOP and you can in Scala you should also be pressing on the functional way of doing things, functional programming states your objects should be immutable and case classes that's what they are for, they represent the value object pattern built right in the language. My advice would be to use composition instead.
case class A(field : Field)
case class B(a : A, moreFields : Fields)
I am not sure how you are trying to persist things to a database.