I have this code:
case class Sorting(field: String = "createdAt", order:
SortingOrder = SortingOrder.Desc)
object Sorting {
sealed trait SortingOrder
object SortingOrder {
case object Desc extends SortingOrder
case object Asc extends SortingOrder
}
def apply(field: Option[String], order: Option[SortingOrder], defaultSortField: String = "createdAt"): Sorting =
Sorting(
field.getOrElse(defaultSortField),
order.getOrElse(SortingOrder.Desc)
)
}
This gives error:
in object Sorting, multiple overloaded alternatives of method apply define default arguments.
Why am I missing here?
Scala compiler will generate default apply method in the companion object which will use your case class constructor parameters i.e. case class Sorting(field: String = "createdAt", order: SortingOrder = SortingOrder.Desc) which has default values. And then you have declared another apply method which has default parameters also and Scala disallows to have overloaded methods with default arguments.
So you need to remove default parameter either from case class (compiles) or from your custom apply method (compiles also) or handle custom default case manually by creating extra apply overload (compiles):
case class Sorting(field: String = "createdAt", order: SortingOrder = SortingOrder.Desc)
object Sorting {
sealed trait SortingOrder
object SortingOrder {
case object Desc extends SortingOrder
case object Asc extends SortingOrder
}
def apply(field: Option[String], order: Option[SortingOrder]): Sorting = apply(field, order, "createdAt")
def apply(field: Option[String], order: Option[SortingOrder], defaultSortField: String): Sorting =
Sorting(
field.getOrElse(defaultSortField),
order.getOrElse(SortingOrder.Desc)
)
}
Related
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 = …
I have defined a method in a trait:
trait myTrait {
def myMethod(entity: Entity = null, name: String)
}
val t = ....//concrete implementation of myTrait
t.myMethod("John")
I have provided default value of null for entity in method definition.
This give compilation error as: Type mismatch, required MyCustomClass found String
Why is it so?
Use the name of your argument.
t.myMethod(name = "John")
It also works for case class when you want to .copy
case class MyStruct(a: String, b: String)
val a = MyStruct("foo", "bar")
val b = a.copy(b = "clazz")
See https://docs.scala-lang.org/tour/named-arguments.html
Also, in scala, the presence / absence of value is usually represented by Option[T] like
def myMethod(entity: Option[Entity] = None, ... )
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
I am trying to understand why the scala enumeration does not pick values from other enum types, when created as a value in it. Say I have the following
trait SchemaBase extends Enumeration {
implicit def valueToVal(x: Value): Val = x.asInstanceOf[Val]
case class Val(name: String, dataType: DataType, nullable: Boolean) extends super.Val {
val col: Column = functions.col(name)
}
protected def column(name: String, dataType: DataType, nullable: Boolean = true): SchemaBase.Val = {
Val(name, dataType, nullable).asInstanceOf[SchemaBase.Val]
}
object SchemaBase extends SchemaBase
Now if I create Enumerations AScehma and BSchema like below:
object ASchema extends SchemaBase {
val Id: SchemaBase.Val = column("a_id", IntegerType)
val BName: SchemaBase.Val = BSchema.Name
}
object BSchema extends SchemaBase {
val Name: SchemaBase.Val = column("b_name", StringType)
}
Here ASchema.BName won't be available in ValueSet of ASchema.
I understand BNameis an enumeration value of BSchema, and know of an approach to solve this problem, but would like to know why this behavior?
Each call to a Value method adds a new unique value to the enumeration.
That should really be "the Value constructor", and of course the call
can be indirect (e.g. column calls SchemaBase.Val constructor which calls Enumeration.Val constructor which calls Enumeration.Value constructor). As the next sentence says, it's simply convention that
these values are usually defined as val members of the enumeration.
You are free to have any other members, or not define some or all values as vals. And
val BName: SchemaBase.Val = BSchema.Name
doesn't call the Value constructor (either ASchema's or BSchema's). That's it.
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...