In scala what is the difference in defining Enumeration as below
object TargetRelation extends Enumeration {
val Organizations = "organizations"
val Individuals = "individuals"
val Things = "things"
}
object TargetRelation extends Enumeration {
type TargetRelation = Value
val Organizations = Value("organizations")
val Individuals = Value("individuals")
val Things = Value("things")
}
My Questions
Which is the preferred way of defining the Enumeration
What is the difference between the first and the second
And if i want to do a case match on a string that returns either of the above values. Which one should i use
What is the difference of having type TargetRelation = Value and not having it in Enumeration
Here is the code snippet
Note : target.resourceType is a String
target.resourceType match {
case TargetRelationType.Individuals => {}
case TargetRelationType.Organizationse => {}
}
This one:
object TargetRelation extends Enumeration {
type TargetRelation = Value
val Organizations = Value("organizations")
val Individuals = Value("individuals")
val Things = Value("things")
}
will use build-in objects and types of Enumeration trait to:
generate names for each Value
assign ordering to each Value
provide methods for parsing String into Value
where TargetRelation.Value is the actual enumeration type (which is why people sometime alias it to companion object name and import directly the alias).
This one:
object TargetRelation extends Enumeration {
val Organizations = "organizations"
val Individuals = "individuals"
val Things = "things"
}
doesn't use Value anywhere so it is virtually the same as this one:
object TargetRelation {
val Organizations = "organizations"
val Individuals = "individuals"
val Things = "things"
}
meaning - this is just namespace for constant Strings.
However, from type safety point of view both are hardly useful as neither can be checked for exhaustiveness.
targetRelation match {
case TargetRelation.Organizations => ...
// no warning or error about which cases are missing
}
If you are in need of something that work like Enum: name parsing, automating name and ordering generation, exhaustiveness checking - both approach are deprecated by community which adopted sealed traits with case objects (and enumeratum) instead:
// this ensured that matching is exhaustive
sealed trait TargetRelation
object TargetRelation {
case object Organizations extends TargetRelation
case object Individuals extends TargetRelation
case object Things extends TargetRelation
}
// like above but with tons of utilities
import enumeratum._
sealed trait TargetRelation extends EnumEntry
object TargetRelation extends Enum[TargetRelation] {
case object Organizations extends TargetRelation
case object Individuals extends TargetRelation
case object Things extends TargetRelation
val values = findValues
}
With that you can use TargetRelation.withName("organizations") during parsing phase (there is a support for Circe and many other libraries) and compare parsed value against case objects of a sealed hierarchy which is type safe, faster etc.
The first one is not an Enumeration. You could leave out extends Enumeration and achieve the same result.
It is just a singleton with a few string constants
Related
I am trying to use an Enumeration object in a method. The enum object extends from a trait, and the method takes the trait as a parameter. Here is the sample code.
sealed trait canAdd
object DAdder extends Enumeration with canAdd
{
type DAdder = Value
val P, Q = Value
}
class ClassTypeTest extends AnyFlatSpec with Matchers
{
class AClass
{
def add(v: canAdd) = if(v.isInstanceOf[DAdder]) println("Got DAdder") else println("Got IAdder")
def note(v: canAdd) = println("Got some canAdd trait object")
}
val aobj = new AClass
val aseq: Seq[DAdder] = Seq(DAdder.P, DAdder.Q, DAdder.P)
//*** Error in below line *****
aseq.foreach(aobj.add(_))
}
The compiler gives the following error:
Error:(23, 23) type mismatch;
found : x.x.DAdder.DAdder
(which expands to) x.x.DAdder.Value
required: x.x.canAdd
aseq.map(aobj.add(_))
Shouldn't I be able to pass an object that inherits the trait in a method that takes the trait as an argument? How do I fix this?
Enumeration class doesn't allow you to extends its values functionality. It is basically:
abstract class Enumeratum {
sealed trait Value { /* utilities */ }
object Value { /* factory of Values */ }
// utilities
}
Since you have the control only on Enumeratum class you cannot extend Values which is what you ask for.
You can easily have this functionality with sealed traits and case objects
sealed trait DAdder extends canadd
object DAdder {
case object P extends DAdder
case object Q extends DAdder
}
This however misses some utilities like finding value by its name, listing all values, etc.
This problem is solved by Enumeratum library which require that you mix in some traits and cope paste one line (val values = findValues) to have all functionalities of Enumeration and more
import enumeratum._
sealed trait DAdder extends canadd
with EnumEntry // mixin here
object DAdder extends Enum[DAdder] { // and mixin here
case object P extends DAdder
case object Q extends DAdder
val values = findValues // and one line calling macro
}
(There are also specializations for enums that should store some primitives/strings as their values in enumeratum.values._).
In Scala 3 this will probably look slightly different as it introduced enum keyword
sealed trait DAdder extends canadd
object DAdder {
case object P extends DAdder
case object Q extends DAdder
}
will become
enum DAdder extends canadd {
case P, Q
}
Scala 3's enum will ordinal method defined, so libraries like Enumeratum will have to provide a little fewer functionalities. (I guess by mixing in Java's Enum trait you will have access to values so everything else is a matter of extension methods).
I'd like to introduce some types to represent possible values of a field in a larger type. This fields needs to be possible to encode/decode to/from JSON and also be able to be written/read to a database.
I'm still new to Scala and the type I would like is the sum type Status = NotVerified | Correct | Wrong. Since I want to have a string representation associated with each constructor, I created a sealed case class with a String parameter and then objects extending that case class. In order to be able to encode/decode, I also need to have implicits, but I'm not sure how to structure this. I could put them in a new object inside the object, like this:
sealed case class Status(name: String)
object Status {
object NotVerified extends Status("not_verified")
object Correct extends Status("correct")
object Wrong extends Status("wrong")
object implicits {
implicit val encodeStatusJson: Encoder[Status] =
_.name.asJson
implicit val decodeStatusJson: Decoder[Status] =
Decoder.decodeString.map(Status(_))
implicit val encodeStatus: MappedEncoding[Status, String] =
MappedEncoding[Status, String](_.name)
implicit val decodeStatus: MappedEncoding[String, Status] =
MappedEncoding[String, Status](Status(_))
}
}
… and then explicitly import these where needed, but that's quite … explicit.
What is a good way of organizing such collections of a type + implicits?
The common approach is to define a sealed trait:
sealed trait Status {
def name: String
}
object Status {
case object NotVerified extends Status {
val name = "not_verified"
}
case object Correct extends Status {
val name = "correct"
}
case object Wrong extends Status {
val name = "wrong"
}
}
Or a sealed abstract class, which may look nicer in the current Scala versions:
sealed abstract class Status(val name: String)
object Status {
case object NotVerified extends Status("not_verified")
case object Correct extends Status("correct")
case object Wrong extends Status("wrong")
}
To avoid the need to import implicits, they can be placed directly in the companion object of the type. See also the question Where does Scala look for implicits? for more details, especially the section Companion Objects of a Type.
And yes, defining implicits for enumerations like that easily gets repetitive. You have to resort to reflection or macros. I recommend using the Enumeratum library, which also has integrations with Circe and Quill.
Here is an example for Circe:
import enumeratum.values._
sealed abstract class Status(val value: String) extends StringEnumEntry {
def name: String = value
}
object Status extends StringEnum[Status] with StringCirceEnum[Status] {
val values = findValues
case object NotVerified extends Status("not_verified")
case object Correct extends Status("correct")
case object Wrong extends Status("wrong")
}
And you can use it without defining any encoders/decoders explicitly or importing anything from Status:
scala> import io.circe.syntax._
scala> val status: Status = Status.Correct
status: Status = Correct
scala> status.asJson
res1: io.circe.Json = "correct"
scala> Decoder[Status].decodeJson(Json.fromString("correct"))
res2: io.circe.Decoder.Result[Status] = Right(Correct)
If you add an apply method you can create the appropriate Status from a String, which should make the Decoder work properly. And making Status abstract
sealed abstract class Status(name: String)
object Status {
object NotVerified extends Status("not_verified")
object Correct extends Status("correct")
object Wrong extends Status("wrong")
def apply(name: String): Status = name match {
case "not_verified" => NotVerified
case "correct" => Correct
case _ => Wrong
}
}
I think your existing implicits will still work, but I don't know those specific libraries...
For pedagogical purposes I have implemented an enumeration using both Enumeration and case objects. I'm comfortable with the Enumeration version, although it cannot warn of missing cases in a match statement.
I have tried to write the equivalent using case objects but I am finding that, although it compiles, it doesn't run. In particular, it seems to go into an infinite loop at run time (I haven't been able to confirm this in the debugger, however). Here's the code I'm trying to run:
sealed abstract class Suit extends Ordered[Suit] {
val name: String
val priority: Int
override def toString = name
def compare(that: Suit) = priority-that.priority
}
case object Spades extends Suit { val name = "Spades"; val priority = 0 }
case object Hearts extends Suit { val name = "Hearts"; val priority = 1 }
case object Diamonds extends Suit { val name = "Diamonds"; val priority = 2 }
case object Clubs extends Suit { val name = "Clubs"; val priority = 3 }
object Suit {
implicit def ordering[A <: Suit]: Ordering[A] = Suit.ordering
def suits = List(Clubs,Spades).sorted
}
If I enter this code into the REPL (via paste mode) or if I compile it in Eclipse, all appears well. But if I try to test the suits method of the Suit object, the run just hangs and I have to terminate it.
I have tried most if not all of the suggestions I've found here on StackOverflow regarding implicits (for instance, not defining Suit class to extend Ordered[Suit] and instead specifying an Ordering directly for the class. None of these have compiled however.
Working suggestions much appreciated.
object Suit {
implicit def ordering[A <: Suit]: Ordering[A] = Suit.ordering
}
That is an infinite loop, ordering calls itself. I think you want an Ordering[Suit], it doesn't make sense in this case to ask for an ordering of a sub-type A of Suit (that would be one individual case object).
There is already an implicit ordering for types that extend Ordered, so you won't even need to construct one:
implicitly[Ordering[Suit]] // aka Ordering.ordered[Suit]
So the following just works:
object Suit {
def suits = List[Suit](Clubs, Spades).sorted
}
Note that List(Clubs, Spades).sorted won't work, because the inferred list element type is Suit with Product and somehow that produces a failure to find an unambiguous ordering.
Part of the problem that I had before was that Suit actually extends a trait called Concept, which I hadn't shown here (in an attempt to simplify things).
However, it actually allows a slight improvement on 0__'s answer because I no longer have to explicitly set the type of my list. Here is the code, at least the relevant parts:
trait Concept extends Ordered[Concept]{
val name: String
val priority: Int
override def toString = name
def compare(that: Concept) = priority-that.priority
}
sealed trait Suit extends Concept {
}
object Concept {
implicit def ordering[A <: Concept]: Ordering[A] = Ordering.by(_.priority)
}
case object Spades extends Suit { val name = "Spades"; val priority = 0 }
case object Hearts extends Suit { val name = "Hearts"; val priority = 1 }
case object Diamonds extends Suit { val name = "Diamonds"; val priority = 2 }
case object Clubs extends Suit { val name = "Clubs"; val priority = 3 }
object Suit {
import scala.math.Ordered.orderingToOrdered
def suits = List(Clubs,Spades).sorted
}
It would be great, if it is possible to extend the Scala enumeration value type in a way, that a kind of tree structure accrues. I mean something like that:
[Examples are just pseudo code!]
object SomeSubEnum extends Enumeration {
type SomeSubEnum = Value
val SubA, SubB, SubC = Value
}
object SomeTopEnum extends Enumeration {
type SomeTopEnum = Value
val TopA = Value("TopA", Subs = List(SomeSubEnum.SubA, SomeSubEnum.SubB))
val TopB = Value("TopB", Subs = List(SomeSubEnum.SubC))
val TopC = Value("TopC", Subs = List(SomeSubEnum.SubA, SomeSubEnum.SubB, SomeSubEnum.SubC))
}
Usage:
def doSomething(param: SomeTopEnum) = {
someTopEnum.Subs.forAll(sub => doMore(sub))
}
doSomethingElse(SomeTopEnum.TopA.Subs.SubB) // Subs could be a map
I am new at Scala, in other languages I would create a kind of static class tree structure to do that, but maybe there is a better solution in Scala.
[sorry for my poor English]
Could you use case classes for this instead? The enumeration type in Scala isn't generally that useful IMO. Something along the lines of (off the top of my head):
sealed abstract class MyEnum[A]
case class SubEnum[A](value : A) extends MyEnum[A]
case class TopEnum[A](value : A, subs : List[SubEnum[A]]) extends MyEnum[A]
This would allow you to do things like:
val top = TopEnum("top value", List(SubEnum("sub value 1"), SubEnum("sub value 2")))
and then:
top.subs map (println(_))
or:
for(s <- top.subs) println(s.value)
Or any pattern matching you might want to do as well. If you wanted to restrict the types of the type parameter (A) then you could define a top level case class which all sub classes must be a subtype of:
sealed abstract class EnumValue(a : String)
case class EnumValue1 extends EnumValue("value 1")
case class EnumValue2 extends EnumValue("value 2")
...etc....
sealed abstract class MyEnum[A <: EnumValue]
case class SubEnum[A <: EnumValue](value : A) extends MyEnum[A]
case class TopEnum[A <: EnumValue](value : A, List[SubEnum[A]]) extends MyEnum[A]
Bit verbose, but will probably do what you want...
Hope this helps
I'd like to "lock" a class, which is extended from a trait. Is it possible in Scala?
For example I have:
trait A {
val boris: String
val john: String
val number: Int
}
class B extends A {
// do something with these values
}
but can I ensure, that in class B no new values will be added if those aren't declared in trait A?
Thanks for your answers.
You cannot.
But if you simply mark the trait as sealed and provide a default implementation:
sealed trait A { val boris: String }
final class B(val boris: String) extends A {}
then people are free to create implicit value classes that make it look like new functionality has been added (except without actually creating the class):
implicit class MyB(val underlying: B) extends AnyVal {
def sirob = underlying.boris.reverse
}
(new B("fish")).sirob // "hsif"
You can also let the classes take a type parameter as a marker if you want to keep them straight at compile-time (though not runtime):
sealed trait A[T] { val boris: String }
final class B[T](val boris: String) extends A[T] {}
implicit class MyB(val underlying: B[Int]) extends AnyVal {
def sirob = underlying.boris.reverse
}
(new B[Int]("fish")).sirob // "hsif"
(new B[Char]("fish")).sirob // error: value sirob is not a member of B[Char]
So you could--especially with 2.10--simply lock everything and let users enrich the original interface this way.
I'm not sure if this covers your intended use case, though; it doesn't provide any inheritance.
Based on your example and my guess at what you are actually trying to do, you may want to consider just using case classes.
Extending a case class is generally avoided (I think it will spit out deprecation warnings if you try), so that will prevent people from wanting to extend your class in order to add functionality.
Translating your example into a case class:
case class A (boris: String, john: String, number: Int)
Then instead of extending A to change its values, you'd just make a new instance, e.g.
val a2 = someOtherA.copy(john="Doe")