How to use Scala Enumerations in Classes - scala

I'm learning Scala and am trying to get a simple enum setup for a project. I've checked a few examples and none seem to fit, all of the examples in the Scala documentation and on StackOverflow are for enums inside objects, not classes. I'm getting an IDE warning that I don't understand. I'm coming from a beginner Java background into Scala, which may be the cause of my confusion.
Here's the code:
class Car(maxSpeed: Integer) {
// Enums
object CarType extends Enumeration {
type CarType = Value
val PERIPHERAL, COMPUTER, EMPTY = Value
}
import CarType._
// Fields
val repeated: CarType
}
When I mouse over the class name, I can see this Intellij warning:
Class 'Car' must either be declared abstract or implement abstract member 'typed: Car.this.CarType.CarType' in 'Car'
I'm not sure why it wants me to implement my variable, and the class is not intended to be abstract. I'd like to use Enums similarly to how they are used in Java.

Move the enumeration outside the class:
// Enums
object CarType extends Enumeration {
type CarType = Value
val PERIPHERAL, COMPUTER, EMPTY = Value
}
class Car(maxSpeed: Integer) {
import CarType._
// Fields
val repeated: CarType
}
Or move it to a companion object:
class Car(maxSpeed: Integer) {
import Car.CarType._
// Fields
val repeated: CarType
}
object Car {
object CarType extends Enumeration {
type CarType = Value
val PERIPHERAL, COMPUTER, EMPTY = Value
}
}
The problem is that things defined inside of a class are scoped to the the instance of that class (unlike some other languages).
That said, I recommend using algebraic data types instead of enums:
sealed trait CarType
object CarType {
case object Peripheral extends CarType // strange choice of names
case object Computer extends CarType
case object Empty extends CarType
}
case class Car(maxSpeed: Int, carType: CarType)
For more info about sealed traits see this SO q&a

What you're looking for is a Scala Case Class.
class Car(maxSpeed: Integer)
case class Minivan(maxSpeed: Integer) extends Car(maxSpeed: Integer)
case class Hodrod(maxSpeed: Integer) extends Car(maxSpeed: Integer)
case class Coupe(maxSpeed: Integer) extends Car(maxSpeed: Integer)
Enumerations as they exist in Java aren't really used in Scala. By having a construction like what I have above, you can leverage Scala's powerful pattern matching do something like this:
val unknownCar = getCar() // Some function that gets a car
unknownCar match {
case Minivan => println("Driving the kids")
case Hodrod => println("Tearing up the drag")
case Coupe => println("Riding low")
}
...while still allowing you to treat it as a Car.
Because they are case classes Scala has a lot of things that help you out.
Note the documentation for Enumeration:
Typically these values enumerate all possible forms something can take and provide a lightweight alternative to case classes.
You should really only use it if you aren't planning to use those values for anything else - but even there case classes will generally serve you better.

Related

how Value type works in enum?

what's a diff between these two snippets
object Main extends Enumeration {
val first : String = "Thriller"
val second : String = "Horror"
val third : String = "Comedy"
val fourth : String = "Romance"
// Main Method
def main(args: Array[String]) {
println(s" Movie Genres = ${Main.first}")
}
}
And
object Main extends Enumeration
{
type Main = Value
// Assigning values
val first = Value("Thriller")
val second = Value("Horror")
val third = Value("Comedy")
val fourth = Value("Romance")
// Main method
def main(args: Array[String])
{
println(s"Main Movie Genres = ${Main.values}")
}
}
what benefit am getting by using type Main = Value in second code snippet ? I'm struggling to understand this even after reading alot online. Please share any simple understanding if possible.
Because in Java when we define this below code, all values (HEARTS, DIAMOND etc) are of CardSuit type, so can't we say that (first, second etc) are of Main type ?
enum CardSuit{
HEARTS,
DIAMONDS,
CLUBS,
SPADES,
}
Last is, Is there any way I could have more than 2 attributes assign to any enum's constants in scala ? For example val first = Value("Thriller", "movie, "london", "production")
Enumeration used private constructors and other tricks to:
define type Value within your object that you extended with Enumeration
limited constructors so that they could only be called from within your object
make these constructors do side-effects so that each new value would append itself to the mutable list (which isn't mutable from outside)
This approach has several issues:
when you do object MyEnum extends Enumeration your enumerations will NOT be defined as MyEnum type, but as MyEnum.Value
if you want to use MyEnum as the name of enumeration, you have to use type alias type MyEnum = MyEnum.Value or type MyEnum = Value
however the type of companion will still be MyEnum.Value.type instead of MyEnum.type. Aliases creating MyEnum.MyEnum and MyEnum.MyEnum.type are just silly
each value is of the same type, so type level programming options are quite limited
you cannot customize Value: let it inherit your type, add some field, store something else than String, not store anything...
Points 1. and 2. are the reasons people did the type aliasing. All 5 were the reasons people moves to sealed traits
sealed trait MyEnum extends Product with Serializable
object MyEnum {
case object One extends MyEnum
case object Two extends MyEnum
case object Three extends MyEnum
}
they work better with pattern matching, derivation, it is easier to customize them (you just add things to common trait/common abstract class), etc.
In Scala 3 it got simplified to
enum MyEnum:
case One, Two, Three
All in all, there is virtually 0 reasons to use Enumeration other than legacy code.

scala value class multiple inheritance

I have in my project objects that represent IDs.
Let's say it is ChairId, TableId, LampId. I want them all to inherit from GenericId. And I want to be able to call def f(x: GenericId) = x.id
I want them to hold only single id: String so I would like to make them extend AnyVal.
Also I would like for each type to provide function generate which would generate my specific ID i.e. I would like to type something like ChairId.generate()
I have typed this:
sealed abstract class GenericId(val id: String)
final case class ChairId(override val id: String) extends GenericId(id)
final case class TableId(override val id: String) extends GenericId(id
And I though if GenericId would inherit from AnyVal that would work but so far no luck ;/ I also tried making GenericId a trait and make case classes extend AnyVal with GenericId but also won't compile :/
Another thing with TableId.generate() I can provide companion object just with function generate and that basically solve my problem but I wondered if there is possibility to solve that without defining companion object? (i.e. through implicits somehow)
// edit
regarding comment to provide code which doesn't compile(and I would like to):
sealed abstract class AbstractId(val id: String) extends AnyVal
final case class CatId(override val id: String) extends AbstractId(id)
final case class DogId(override val id: String) extends AbstractId(id)
Value classes cannot work this way for a couple of reasons.
First, from the documentation, value classes cannot be extended by any other class, so AbstractId cannot extend AnyVal. (Limitation #7)
scala> abstract class AbstractId(val id: String) extends AnyVal
<console>:10: error: `abstract' modifier cannot be used with value classes
abstract class AbstractId(val id: String) extends AnyVal
^
Second, even if you make AbstractId a trait, and define the other ids like this:
final case class DogId(val id: String) extends AnyVal with AbstractId
.. the usage of the value class wouldn't fit your case, because the class itself would still get allocated. See the allocation summary:
A value class is actually instantiated when:
a value class is treated as another type.
a value class is assigned to an array.
doing runtime type tests, such as pattern matching.
Some quotes from the value classes SIP that are likely to clarify your doubts:
Value classes...
...must have only a primary constructor with exactly one public, val
parameter whose type is not a value class.
... cannot be extended by another class.
As per 1. it can not be abstract; per 2. your encoding doesn't work.
There is another caveat:
A value class can only extend universal traits and cannot be extended
itself. A universal trait is a trait that extends Any, only has defs
as members, and does no initialization. Universal traits allow basic
inheritance of methods for value classes, but they incur the overhead
of allocation.
With all that in mind, based on your last snippet, this might work:
sealed trait AbstractId extends Any { def id: String }
final case class CatId(id: String) extends AnyVal with AbstractId
final case class DogId(id: String) extends AnyVal with AbstractId
But keep in mind the allocation only occurs if you want to use CatId and DogId as an AbstractId. For better understanding I recommend reading the SIP.

Scala: Value Class vs Case Class

I'm trying to discover the differences between using a value class or a case class in a given scenario. Suppose I want to model the integers mod 5 as a unique datatype. The question is which one I should begin with...
class IntegerMod5(val value: Int) extends AnyVal
case class IntegerMod5(value: Int)
Regardless, it seems that I can create an implementation of Numeric fairly easily. With the case class approach, then, I can simply do this:
case class IntegerMod5(value: Int)(implicit ev: Numeric[IntegerMod5]) {
import ev.mkNumericOps
}
However, it seems to be a much more difficult endeavour with value classes, mainly as the benefit is to avoid object creation. Thus, something like
implicit class IntegersMod5Ops(value: IntegerMod5)(implicit ev: Numeric[IntegerMod5]) {
import ev.mkNumericOps
}
Would appear to largely defeat the purpose. (Not sure if it even works, actually.)
The question is that is it possible to use Numeric with a value class, or will I have to bite the bullet and use a case class?
You don't need implicit ev: Numeric[IntegerMod5] as an argument, just define it in the companion object:
object IntegerMod5 {
implicit val numeric: Numeric[IntegerMod5] = ...
}
It will be automatically picked up when you use arithmetic operations on IntegerMod5s, and because it's a val, it's only initialized once (you can use object as well).

Custom Scala enum, most elegant version searched

For a project of mine I have implemented a Enum based upon
trait Enum[A] {
trait Value { self: A =>
_values :+= this
}
private var _values = List.empty[A]
def values = _values
}
sealed trait Currency extends Currency.Value
object Currency extends Enum[Currency] {
case object EUR extends Currency
case object GBP extends Currency
}
from Case objects vs Enumerations in Scala. I worked quite nice, till I run into the following problem. Case objects seem to be lazy and if I use Currency.value I might actually get an empty List. It would have been possible to make a call against all Enum Values on startup so that the value list would be populated, but that would be kind of defeating the point.
So I ventured into the dark and unknown places of scala reflection and came up with this solution, based upon the following SO answers. Can I get a compile-time list of all of the case objects which derive from a sealed parent in Scala?
and How can I get the actual object referred to by Scala 2.10 reflection?
import scala.reflect.runtime.universe._
abstract class Enum[A: TypeTag] {
trait Value
private def sealedDescendants: Option[Set[Symbol]] = {
val symbol = typeOf[A].typeSymbol
val internal = symbol.asInstanceOf[scala.reflect.internal.Symbols#Symbol]
if (internal.isSealed)
Some(internal.sealedDescendants.map(_.asInstanceOf[Symbol]) - symbol)
else None
}
def values = (sealedDescendants getOrElse Set.empty).map(
symbol => symbol.owner.typeSignature.member(symbol.name.toTermName)).map(
module => reflect.runtime.currentMirror.reflectModule(module.asModule).instance).map(
obj => obj.asInstanceOf[A]
)
}
The amazing part of this is that it actually works, but it is ugly as hell and I would be interested if it would be possible to make this simpler and more elegant and to get rid of the asInstanceOf calls.
Here is a simple macro based implementation:
import scala.language.experimental.macros
import scala.reflect.macros.blackbox
abstract class Enum[E] {
def values: Seq[E] = macro Enum.caseObjectsSeqImpl[E]
}
object Enum {
def caseObjectsSeqImpl[A: c.WeakTypeTag](c: blackbox.Context) = {
import c.universe._
val typeSymbol = weakTypeOf[A].typeSymbol.asClass
require(typeSymbol.isSealed)
val subclasses = typeSymbol.knownDirectSubclasses
.filter(_.asClass.isCaseClass)
.map(s => Ident(s.companion))
.toList
val seqTSymbol = weakTypeOf[Seq[A]].typeSymbol.companion
c.Expr(Apply(Ident(seqTSymbol), subclasses))
}
}
With this you could then write:
sealed trait Currency
object Currency extends Enum[Currency] {
case object USD extends Currency
case object EUR extends Currency
}
so then
Currency.values == Seq(Currency.USD, Currency.EUR)
Since it's a macro, the Seq(Currency.USD, Currency.EUR) is generated at compile time, rather than runtime. Note, though, that since it's a macro, the definition of the class Enum must be in a separate project from where it is used (i.e. the concrete subclasses of Enum like Currency). This is a relatively simple implementation; you could do more complicated things like traverse multilevel class hierarchies to find more case objects at the cost of greater complexity, but hopefully this will get you started.
A late answer, but anyways...
As wallnuss said, knownDirectSubclasses is unreliable as of writing and has been for quite some time.
I created a small lib called Enumeratum (https://github.com/lloydmeta/enumeratum) that allows you to use case objects as enums in a similar way, but doesn't use knownDirectSubclasses and instead looks at the body that encloses the method call to find subclasses. It has proved to be reliable thus far.
The article "“You don’t need a macro” Except when you do" by Max Afonov
maxaf describes a nice way to use macro for defining enums.
The end-result of that implementation is visible in github.com/maxaf/numerato
Simply create a plain class, annotate it with #enum, and use the familiar val ... = Value declaration to define a few enum values.
The #enum annotation invokes a macro, which will:
Replace your Status class with a sealed Status class suitable for acting as a base type for enum values. Specifically, it'll grow a (val index: Int, val name: String) constructor. These parameters will be supplied by the macro, so you don't have to worry about it.
Generate a Status companion object, which will contain most of the pieces that now make Status an enumeration. This includes a values: List[Status], plus lookup methods.
Give the above Status enum, here's what the generated code looks like:
scala> #enum(debug = true) class Status {
| val Enabled, Disabled = Value
| }
{
sealed abstract class Status(val index: Int, val name: String)(implicit sealant: Status.Sealant);
object Status {
#scala.annotation.implicitNotFound(msg = "Enum types annotated with ".+("#enum can not be extended directly. To add another value to the enum, ").+("please adjust your `def ... = Value` declaration.")) sealed abstract protected class Sealant;
implicit protected object Sealant extends Sealant;
case object Enabled extends Status(0, "Enabled") with scala.Product with scala.Serializable;
case object Disabled extends Status(1, "Disabled") with scala.Product with scala.Serializable;
val values: List[Status] = List(Enabled, Disabled);
val fromIndex: _root_.scala.Function1[Int, Status] = Map(Enabled.index.->(Enabled), Disabled.index.->(Disabled));
val fromName: _root_.scala.Function1[String, Status] = Map(Enabled.name.->(Enabled), Disabled.name.->(Disabled));
def switch[A](pf: PartialFunction[Status, A]): _root_.scala.Function1[Status, A] = macro numerato.SwitchMacros.switch_impl[Status, A]
};
()
}
defined class Status
defined object Status

scala case object pollution

Let me explain ;-)
both classes below are in package com.company.foo
RoleGroup.scala
abstract class RoleGroup
case object ADMIN extends RoleGroup
case object MEMBER extends RoleGroup
MailSender.scala
abstract class MailSender
case object ADMIN extends MailSender
case object STAFF extends MailSender
case object ACCOUNTANT extends MailSender
The problem here is that ADMIN is ambiguous since there is no namespace separation with case objects. It seems there can only be one uniquely named case object per package.
I suppose I could name case objects based on function a la mailADMIN, roleADMIN and so on. Or should I just create proper Enums and forget about case objects? Or take some other approach?
you could do something like:
object RoleGroup {
sealed trait RoleGroup
case object ADMIN extends RoleGroup
case object MEMBER extends Rolegroup
}
and similarly for MailSender. Then in situations where you're only using one, you can do import MailSender._ or vise versa, but when using both you refer to them as RoleGroup.ADMIN, etc.
Whether you want to take this approach or use Enums mostly depends on how you intend to use them. In this aproach, each "enum" is a type, whereas with Enums each enum is a value. The former works better for pattern matching since the compiler can check if your matches are exhaustive, the latter is better (IMO) for working with serialization.
You don't need enums here, as much as probably anywhere else. All you need is proper namespacing. I find the companion object approach to be the most benefitial:
sealed abstract class RoleGroup
object RoleGroup {
case object Admin extends RoleGroup
case object Member extends RoleGroup
// also just in case
sealed case class SomeParameterizedGroup (
param1: Any,
param2: Int
) extends RoleGroup
}
Using it this way will very much remind you of Java's enums, while also providing you with strengths of Scala:
def foo (roleGroup: RoleGroup) =
roleGroup match {
case RoleGroup.Admin =>
case RoleGroup.SomeParameterizedGroup(param1, param2) =>
}
Please note that in Dan's solution the type of roleGroup would be RoleGroup.RoleGroup, which kinda feels unnatural. Also note that uppercase names violate Scala's style conventions and from examples above you can kinda see why.