Scala: Inherited enum object doesn't satisfy parent trait - scala

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).

Related

How do I limit the parameter types of a generic argument based on another parameter in Scala?

I'm trying to create a map of different configurations, where each configuration has a given key object and some options, e.g.
FirstConfig can be either:
FirstConfigOptionA
FirstConfigOptionB
SecondConfig can be either:
SecondConfigOptionA
SecondConfigOptionB
...
And I'm having trouble with general typing and signature of the setter function so it checks at compile time I'm supplying the correct objects, e.g.
// 1. this should compile normally
set(FirstConfig, FirstConfigOptionA)
// 2. should NOT compile due to `SecondConfigOptionA` parameter not being a valid option for `FirstConfig`
set(FirstConfig, SecondConfigOptionA)
So far, my attempts still allow the second case above to compile.
abstract sealed class Configuration
trait OptionKey[T <: Configuration] {}
trait OptionVariant[T <: Configuration] {}
// First Config
trait FirstConfig extends Configuration
object FirstConfigKey extends OptionKey[FirstConfig];
object FirstConfigOptionA extends OptionVariant[FirstConfig]
object FirstConfigOptionB extends OptionVariant[FirstConfig]
// Second Config
trait SecondConfig extends Configuration
object SecondConfigKey extends OptionKey[SecondConfig];
object SecondConfigOptionA extends OptionVariant[SecondConfig]
object SecondConfigOptionB extends OptionVariant[SecondConfig]
def set[T](k: OptionKey[T], v: OptionVariant[T]): Unit = {}
set(FirstConfigKey, FirstConfigOptionA)
set(FirstConfigKey, SecondConfigOptionA) // This still compiles
I've also tried using Enumerations with similar results:
object FirstConfig extends Enumeration {
type FirstConfig = Value
val FirstConfigOptionA, FirstConfigOptionB = Value
}
object SecondConfig extends Enumeration {
type SecondConfig = Value
val SecondConfigOptionA, SecondConfigOptionB = Value
}
def set(k: Enumeration, v: Enumeration#Value): Unit = {}
set(FirstConfig, FirstConfig.FirstConfigOptionA)
set(FirstConfig, SecondConfig.SecondConfigOptionA) // This still compiles
What is the correct way to express this relationship between a config and its available options or what should be set's signature to enforce it?
What about using path-dependant types like this:
trait Configuration {
sealed trait OptionKey
val key: OptionKey
sealed trait OptionVariant
}
object Configuration {
def set(config: Configuration)(variant: config.OptionVariant): Unit = {
println(s"${config} - ${config.key} - ${variant}")
}
}
case object FirstConfig extends Configuration {
private case object FirstConfigKey extends OptionKey
override final val key: OptionKey = FirstConfigKey
case object FirstConfigOptionA extends OptionVariant
case object FirstConfigOptionB extends OptionVariant
}
case object SecondConfig extends Configuration {
private case object SecondConfigKey extends OptionKey
override final val key: OptionKey = SecondConfigKey
case object SecondConfigOptionA extends OptionVariant
case object SecondConfigOptionB extends OptionVariant
}
You can see it working as expected here.
Why do you need to store them as key/value pairs? You can just represent it as an algebraic data type:
enum FirstConfig:
case OptionA
case OptionB
enum SecondConfig:
case OptionA
case OptionB
enum Config:
case First(value: FirstConfig)
case Second(value: SecondConfig)
def set(config: Config): Unit = …

In Scala, how can an Inner case class consistently override a method?

I recently discovered that Scala compiler has an interesting feature for case class: Since it generates both a class & an object signature, if defined as an inner class, it can be used to override an abstract type definition and a function definition of its super class with minimal boilerplate code, here is an example:
object InnerCaseClassOverridingBoth {
trait AALike
trait SS {
type AA <: AALike
def AA(): AnyRef
}
trait SS_Clear extends SS {
def AA(): AnyRef
}
class SSA extends SS_Clear {
case class AA() extends AALike
}
object SSA extends SSA {}
}
This will compile without any error. However the shortcut stops here, if the function definition def AA is parameterized, then neither the inner case class nor inner object is capable of overriding it: the apply function of the inner object doesn't automatically expand to a method of its outer class:
trait SS_Parameterised extends SS {
def AA(ii: Int): AnyRef
}
class SSB extends SS_Parameterised {
case class AA(ii: Int) extends AALike
}
object SSB extends SSB {}
This gives an error:
class SSB needs to be abstract, since method AA in trait
SS_Parameterised of type (ii: Int)AnyRef is not defined
class SSB extends SS_Parameterised {
My question is, is there a shortcut in this case? Why is the Scala compiler is designed to link case 1 but not case 2?
It's not particularly designed at all; or, it is, but not in the way you seem to think. You aren't overriding def AA() with a method that constructs AA, you are overriding it with the object AA itself. Notice
trait T {
type I <: AnyRef
def I(): AnyRef
}
object O extends T {
case class I(val i: Int)
}
This works fine.
> (O: T).I()
I
> (O: T).I().getClass
class O$I$
> O.I(5)
I(5)
> O.I(5).getClass
class O$I
The salient design choices are "objects can override no-param defs" (and so can vals, vars and, of course, no-param defs) and "case classes automatically generate objects". "Inner case classes override methods of the same name in their outer class with their constructors," is not one of Scala's rules. object O contains a case class I and an object I, and the abstract def I(): AnyRef is overridden to return said object I. The contents of object I don't matter, because def I() only has to return an AnyRef, which means no restrictions are imposed. It makes perfect sense that
trait U {
type I <: AnyRef
def I(i: Int): AnyRef
}
object P extends U {
case class I(i: Int)
}
fails, then. object P contains a case class I and an associated object I, but it also needs a def I(i: Int): AnyRef, which it lacks.
I am guessing it is simply related to the role apply plays in case classes. See Case Class default apply method
SSA satisfies SS_Clear.AA via companion object of SSA (SSA.apply).
When you add a parameter to the method you no longer have the 0-parameter apply method to fulfill that role.
OK I found 2 ways of doing this
Method 1: overriden by case class:
trait SS_Parameterised {
type AA <: AALike
def AA: Int => AnyRef
}
Method 2: overriden by implicit class:
trait SS_Parameterised {
type AA <: AALike
implicit def AA(ii: Int): AnyRef
}
class SSB extends SS_Parameterised {
implicit class AA(ii: Int) extends AALike
}
End of story :) One case class overriding 2 declarations? No problem.
(Method 2 works as scala internally generates an implicit function for every implicit class)

scala constrain type of list objects by object property type

Assuming a class like this:
sealed trait ParentTrait
sealed trait Trait1 extends ParentTrait
sealed trait Trait2 extends ParentTrait
...
case object O1 extends extends Trait1
case object O2 extends extends Trait1
....
....
case object Oi extends Trait2
case object Oj extends Trait2
case class A {
... fields ...
val someField: ParentTrait
}
How can I constrain a function to receive objects of A with someField of type Trait2?
i.e.
def myFunction(seq : Seq[A where A.someField is a Trait2] = {
here each item.someField in seq is a Trait2 type
}
I might misunderstand the question somehow, but if you would like to constrain a function to receive case classes that contain only certain element types, the following would do:
scala> :paste
// Entering paste mode (ctrl-D to finish)
sealed trait ParentTrait
sealed trait Trait1 extends ParentTrait
sealed trait Trait2 extends ParentTrait
case object O1 extends Trait1
case object O2 extends Trait1
case object Oi extends Trait2
case object Oj extends Trait2
case class A[PT <: ParentTrait](someField: PT)
def myfunc[PT <: ParentTrait](seq: Seq[A[PT]]) : Unit = ???
val st1: Seq[A[Trait1]] = Seq(A(O1), A(O2))
val st2: Seq[A[Trait2]] = Seq(A(Oi), A(Oj))
myfunc[Trait2](st2)
myfunc[Trait2](st1)
// Exiting paste mode, now interpreting.
<pastie>:31: error: type mismatch;
found : Seq[A[Trait1]]
required: Seq[A[Trait2]]
myfunc[Trait2](st1)
^
This is not possible. Type checking is done at compile time but the concrete type of someField is not known until run time. All the compiler knows is that it is a sub-class of ParentTrait, it cannot know which one will be present when the function is called.
You can either use match in myFunction to check that you have the type you want, or create two variants of A for the two possible types of someField.
Unless there is a strong reason for needing static type checking here, I would suggest using match and having the unit test framework detect accidental use of the function with the "wrong" value of someField. If you go down the variants/generics route then your code is going to get very complicated very quickly.

Passing case objects in Scala

I have the following definitions:
sealed trait MyTrait
case object One extends MyTrait
case object Two extends MyTrait
object Test extends (MyTrait => MyTraitReturnVal) {
def apply(myTrait: MyTrait) = { ... }
def myMethod(myTrait: MyTrait) = {
...
}
}
When I call Test(One), it complains that it is expecting an interface instead of a concrete type. Any suggestions on how to get around this?
So calling:
Test(One)
Complains that it is expecting MyTrait and the actual parameter is One.type!
You are inheriting Test object from Function1 class, so you need to implement 'apply' method instead of 'myMethod'. This code compiles and runs:
sealed trait MyTrait
case object One extends MyTrait
case object Two extends MyTrait
case class MyTraitReturnVal(my: MyTrait)
object Test extends (MyTrait => MyTraitReturnVal) {
def apply(myTrait: MyTrait) =
new MyTraitReturnVal(myTrait)
}
println(Test(One))
println(Test(Two))

Using case object to satisfy abstract type member

I have an architecture where I have a concept of a Component that is instantiated dynamically at run-time from a static configuration and/or run-time data. The static part of the data is provided as a Definition type, which each component must override within its companion object, along with a configurationReads to deserialize it from JSON.
import play.api.libs.json._
trait ComponentDefinition // Intentionally empty
trait ComponentCompanion {
type Definition <: ComponentDefinition
def configurationReads: Reads[Definition]
}
For components with static options, I supply a case class for the Definition type:
class ParticularComponent { /* blah */ }
object ParticularComponent extends ComponentCompanion {
case class Definition(option1: String, option2: Boolean) extends ComponentDefinition
val configurationReads = Json.reads[Definition]
}
This works great. In the past, I've used an empty case class for components that have no static options, but I know that a case object would be more idiomatic. Unfortunately, it doesn't seem to work. If I try:
class OptionlessComponent { /* blah */ }
object OptionlessComponent extends ComponentCompanion {
case object Definition extends ComponentDefinition
val configurationReads = Reads.pure(Definition)
}
I get the following error:
<console>:19: error: overriding method configurationReads in trait ComponentCompanion of type => play.api.libs.json.Reads[OptionlessComponent.Definition];
value configurationReads has incompatible type
val configurationReads = Reads.pure(Definition)
Is there a way to make this work with case object in analogy to the case class version?
I think the case object is creating a new Definition name in the value namespace, but not overloading the abstract type Definition in the type namespace. So this compiles:
class OptionlessComponent { /* blah */ }
object OptionlessComponent extends ComponentCompanion {
case object Definition extends ComponentDefinition
type Definition = Definition.type
val configurationReads = Reads.pure(Definition)
}
Obviously, this isn't exactly an unqualified win over the empty params case class version:
class OptionlessComponent { /* blah */ }
object OptionlessComponent extends ComponentCompanion {
case class Definition() extends ComponentDefinition
val configurationReads = Reads.pure(Definition())
}
If I'm missing something that would allow me to combine the elegance of both of these options, I'm open to suggestions.