I'd like replace all enum field value1 to value2, using Quicklens the compiler says
scalac: Error while emitting
lazy value MyEnum$module
the code:
object MyEnum extends Enumeration {
type MyEnum = Value
val value1, value2 = Value
}
import com.softwaremill.quicklens._
import MyEnum._
case class Foo(name: List[MyEnum])
case class Bar(address: Foo, age: Int)
val bar = Bar(Foo(List(value1)), 35)
val b: Bar = bar.modify(_.address.name.eachWhere(_ == value1)).setTo(value2)
Related
I have the following use case. User is providing two kinds of strings type and growth I need to convert the value of each into an integer from a known map and return the sum of both value.
What I did to map the strings is:
object Types extends Enumeration {
val T1 = 5
val T2 = 6
// list of values is really long!
val TYPE1 = "type1"
val TYPE2 = "type2"
}
object Growth extends Enumeration {
val G1 = 1
val G2 = 10
// list of values is really long!
val GROWTH1 = "growth1"
val GROWTH2 = "growth2"
}
Now consider user is calling sum("type1", "growth2") the expected output is 15 because 5+10=15
I wrote this skeleton function but I'm having trouble utilizing the enumeration objects. How can I do this without having so many if/else?
def sum(type: String, growth: String) : Int = {
var sum:Int = 0
????
return sum
}
if my approach is flawed I'm happy to try another
scala.Enumeration doesn't let you use syntax like this
object MyEnum extends Enumeration {
val value = "string"
}
It actually requires you to do something more like
object MyEnum extends Enumeration {
val value = Value("string")
}
and then you are passing not String but MyEnum.Value.
Since you are giving up on passing around Strings (at least in Scala 2, in Scala 3 you have an option to use type MyEnum = "value1" | "value2" and/or opaque type MyEnum), you create a sealed hierarchy instead
sealed abstract class Type(
val name: String,
val number: Int
) extends Product with Serializable
object Type {
case object Type1 extends Type("type1", 5)
case object Type2 extends Type("type2", 6)
}
sealed abstract class Growth(
val name: String,
val number: Int
) extends Product with Serializable
object Growth {
case object Growth1 extends Growth("growth1", 1)
case object Growth2 extends Growth("growth2", 10)
}
def sum(`type`: Type, growth: Growth) : Int =
`type`.number + growth.number
If you needed to construct a collection of all possible values, instead of scala.Enumeration you can use Enumeratum library instead
import enumeratum.values._
// IntEnumEntry and IntEnum specialize for Int
// and it allows to eliminate boxing
// The Int has to be defined as `val value: Int`
// findValues has to called so that the compiler
// can find all instances in the compile-time and
// populate the `values` collection
// Enumeratum then generate a lot of useful methods out of
// this list e.g. `withName` to find enum by its name
sealed abstract class Type(
val value: Int
) extends IntEnumEntry
object Type extends IntEnum[Type] {
case object Type1 extends Type(5)
case object Type2 extends Type(6)
val values = findValues
}
sealed abstract class Growth(
val value: Int
) extends IntEnumEntry
object Growth extends IntEnum[Growth] {
case object Growth1 extends Growth(1)
case object Growth2 extends Growth(10)
val values = findValues
}
def sum(`type`: Type, growth: Growth) : Int =
`type`.value + growth.value
This can be done by create a Map that gives the score for each String:
val scoreMap = Map(
"type1" -> 5,
"type2" -> 6,
"growth1" -> 1,
"growth2" -> 10,
)
The score can then be computed using foldLeft:
def score(strings: List[String]) =
strings.foldLeft(0){ case (sum, str) => sum + scoreMap.getOrElse(str, 0) }
score(List("type1", "growth2")) // 15
Values that are missing from the Map have the default value 0.
You can extend the class Val of Enumeration class.
Example:
object Types extends Enumeration {
final case class Pairing (label: String, value: Int) extends Val (value, label)
val TYPE1 = Pairing ("type1", 5)
val TYPE2 = Pairing ("type2", 6)
}
object Growths extends Enumeration {
final case class Pairing (label: String, value: Int) extends Val (value, label)
val GROWTH1 = Pairing ("growth1", 1)
val GROWTH2 = Pairing ("growth2", 10)
}
type Type = Types.Pairing
type Growth = Growths.Pairing
def sum(t: Type, g: Growth) = t.id + g.id
// usage:
import Types._
import Growths._
println(sum(TYPE1, GROWTH2)) // => 15
If you wish to use the operator + and reuse the val names :
object Types extends Enumeration {
final case class Entry (value: Int) extends Val (value) {
def +(g: Growth) = id + g.id
}
val type1 = Entry (5)
val type2 = Entry (6)
}
object Growths extends Enumeration {
final case class Entry (value: Int) extends Val (value) {
def +(t: Type) = t.id + id
}
val growth1 = Entry (1)
val growth2 = Entry (10)
}
type Type = Types.Entry
type Growth = Growths.Entry
// usage:
import Types._
import Growths._
println(type1 + growth2) // => 15
println(growth1 + type2) // => 7
println(growth1) // => "growth1"
I shall explain my question with example as shown below.
import scala.reflect.ClassTag
trait LivingBeing extends Product { def name:String; def age:Int}
case class Person (name:String, age:Int) extends LivingBeing
case class Cat(name: String, age:Int) extends LivingBeing
// usual way of creating a case class instance
val john = Person("john", 23)
// Creating a case class instance with tuples
val garfield = Cat tupled ("Garfield", 8)
// create a generic function
def createLivingBeing[T<: LivingBeing](name:String, age:Int)(implicit evidence: ClassTag[T]): T = {
T tupled (name, age) // Does not compile; why?
}
How can one elegantly construct different case classes (that are of a certain trait) generically, given a type and values for its fields?
Consider type class solution
trait LivingBeingFactory[T <: LivingBeing] {
def apply(name: String, age: Int): T
}
object LivingBeingFactory {
implicit val personFactory: LivingBeingFactory[Person] =
(name: String, age: Int) => Person(name, age)
implicit val catFactory: LivingBeingFactory[Cat] =
(name: String, age: Int) => Cat(name, age)
}
def createLivingBeing[T <: LivingBeing](name:String, age:Int)(implicit evidence: LivingBeingFactory[T]): T = {
evidence(name, age)
}
createLivingBeing[Person]("Picard", 70)
// res0: Person = Person(Picard,70)
createLivingBeing[Cat]("Bob", 5)
// res1: Cat = Cat(Bob,5)
// Creating a case class instance with tuples
val garfield = Cat tupled ("Garfield", 8)
...which is the equivalent of...
val garfield = (Cat.apply _).tupled(("Garfield", 8))
This, on the other hand...
T tupled (name, age) // Does not compile; why?
...produces Error: not found: value T because T is a type, not a value. Cat is both a type and a value. It is the type specified for the class, but it is also the companion object to the class Cat. All case classes have a companion object with an apply() method. The compiler knows the difference between them and it knows where one can be used/referenced but not the other.
Imagine a union in thrift like:
union MyUnion {
1. bool myBool
2. i64 myLong
3. double myDouble
}(persisted='true')
What I'd like to do is something like this in Scala:
case class MyClass(
myString: String,
myUnionType: ???
)
Then, I'd like to instantiate this class like:
import ...{thriftscala => thrift}
val myClass = MyClass("cool", thrift.MyUnion.MyBool)
val myOtherClass = MyClass("wow", thrift.MyUnion.MyLong)
Note that I am not instantiating MyBool or MyLong, I just want to pass them as "types". I.e., I am not doing:
val myClass = MyClass("cool", thrift.MyUnion.MyBool(_))
I'm curious if there is a way to do this using the thrift-generated Scala.
You can use an ADT like this :
scala> sealed trait MyUnion
defined trait
MyUnion
This is your base class and then extends with case object :
scala> case object MyBool extends MyUnion
defined object MyBool
scala> case object MyDouble extends MyUnion
defined object MyDouble
scala> case object MyLong extends MyUnion
defined object MyLong
scala> case class MyClass(str : String, union : MyUnion)
defined class MyClass
How use it :
scala> MyClass("cool", MyBool)
res0: MyClass = MyClass(cool,MyBool)
I have several fields that i want to create enum from but those fields have 2 values: Valid and Not valid:
object MyEnum extends Enumeration
{
val
type1, // this type is valid
type2, // valid
type3, // valid
type4, // Not valid
type5, // Not valid
type6, // Not valid
type7 // Not valid
= value
}
Any suggestion how to distinguish between Valid and Not valid via code ? or maybe it must separate into 2 enums ?
You can create a function on the Enum object and use the .toString part of Enumeration.Value
object MyEnum extends Enumeration {
val type1 = Value("valid")
val type2 = Value("valid")
val type3 = Value("valid")
val type4 = Value("Not valid")
val type5 = Value("Not valid")
val type6 = Value("Not valid")
val type7 = Value("Not valid")
lazy val lookup = this.values.map { n => (n, n.toString == "valid") }.toMap
def isValid(myEnum: MyEnum.Value) = lookup.getOrElse(myEnum, false)
}
MyEnum.isValid(MyEnum.type1) //> res0: Boolean = true
MyEnum.isValid(MyEnum.type7) //> res1: Boolean = false
Sources:
https://github.com/scala/scala/blob/v2.11.7/src/library/scala/Enumeration.scala#L146
http://www.scala-lang.org/api/current/#scala.Enumeration$Value
There's a different pattern you could use that does not extend Enumeration but acts similarly:
object MyEnum extends Serializable {
abstract sealed class MyEnum(
val ordinal: Int, val isValid: Boolean
) extends Ordered[MyEnum] with Serializable {
def compare(that: MyEnum) = that.ordinal compare this.ordinal
def toInt: Int = this.ordinal
}
case object Type1 extends MyEnum(0, true)
case object Type2 extends MyEnum(1, true)
case object Type3 extends MyEnum(2, true)
case object Type4 extends MyEnum(3, false)
case object Type5 extends MyEnum(4, false)
case object Type6 extends MyEnum(5, false)
case object Type7 extends MyEnum(6, false)
def fromInt(i: Int): MyEnum = values.find(_.ordinal == i).get
val values = Set(
Type1, Type2, Type3, Type4, Type5, Type6, Type7
)
}
This way you are able to assign in a way like val myEnum: MyEnum.MyEnum = MyEnum.Type1 similar to your example above and you will have access to myEnum.isValid. Similarly you can access it ordinally with the fromInt method. Such as MyEnum.fromInt(0).isValid. If you provide some more context on what you're trying to do with the values I may be able to provide a better solution. Hope this helps.
Given the following Enumeration...
object MyEnum extends Enumeration {
type MyEnum = Value
val Val1 = Value("val1")
val Val2 = Value("val2")
val Val3 = Value("val3")
}
import MyEnum._
... and the following Map...
val m = Map(
val1 -> "one",
val2 -> "two",
val3 -> "three"
)
... I need to transform m to JSON:
import play.api.libs.json._
val js = Json.toJson(m)
The last statement doesn't compile because the compiler doesn't find a Json serializer for type scala.collection.immutable.Map[MyEnum.Value,String].
Question: Since Play does provide a serializer for type scala.collection.immutable.Map[String,String], and my enumeration actually contains strings, is there a way to reuse the default JSON serializer?
The built-in Reads objects don't define a Reads for a Map that's parametric in the key. You could do something like:
implicit def looserMapWrites[A <: AnyRef, B : Writes]: Writes[Map[A, B]] = Writes { o =>
Json.toJson(o.map { case (key, value) =>
key.toString -> value
})(Writes.mapWrites)
}
And with your values above, you get:
scala> Json.toJson(m)(looserMapWrites)
res1: play.api.libs.json.JsValue = {"val1":"one","val2":"two","val3":"three"}
If you want to, you can tighten those bounds on A to make it not applicable to any AnyRef.
You need to define a play.api.libs.json.Format for MyEnum.Value which translates your enum to and from a string representation. Such as the following:
import play.api.libs.json._
object MyEnum extends Enumeration {
type MyEnum = Value
val Val1 = Value("val1")
val Val2 = Value("val2")
val Val3 = Value("val3")
implicit val myEnumWrites = new Writes[Value]{
def writes(o:Value)=JsString(o.toString)
}
}
a more complete format (read and write) for MyEnum could look like
implicit val myEnumFormat = new Format[Value]{
def writes(o:Value)=JsString(o.toString)
def reads(json:JsValue):JsResult[Value]=json match {
case JsString("val1") =>JsSuccess(Val1)
case JsString("val2") =>JsSuccess(Val2)
case JsString("val3") =>JsSuccess(Val3)
case other => JsError(s"$other is not a valid value for MyEnum")
}
}
Using Enumeration is not advised. It can generally be replaced by either a sealed trait and a hierarchy of case object/case classes OR by using a java enumeration.