Comparing String and Enumeration - scala

I have an enumeration in scala mapped to strings in JPA. For more comfortable coding, I defined implicit conversions between them. So I now can define value val person.role = "User", - person.role is the enumeration type "User" a String so there's the conversion. But when I try to compare these two, I always get false, because the def equals (arg0: Any) : Boolean takes Any so there's not any conversion triggered. I need some explicit conversion, but my plan was to be able to omit that, what do you think is the best practice | neatest solution here?

Expanding on Thomas's answer, if you're using the comparison to branch, using pattern matching may be more appropriate:
object Role extends Enumeration {
val User = MyValue("User")
val Admin = MyValue("Admin")
def MyValue(name: String): Value with Matching =
new Val(nextId, name) with Matching
// enables matching against all Role.Values
def unapply(s: String): Option[Value] =
values.find(s == _.toString)
trait Matching {
// enables matching against a particular Role.Value
def unapply(s: String): Boolean =
(s == toString)
}
}
You can then use this as follows:
def allowAccess(role: String): Boolean = role match {
case Role.Admin() => true
case Role.User() => false
case _ => throw ...
}
or
// str is a String
str match {
case Role(role) => // role is a Role.Value
case Realm(realm) => // realm is a Realm.Value
...
}

The Value("User") in your Enumeration is of type Val. And I believe it's implementation of equals does not compare the string name of the value. I think one heavy handed way of doing this is creating your own Enumeration and Val so that it returns true if the name match.
But in my code uses, not with JPA, I always convert the string into the MyEnumeration.Value. This is easy with things like:
object E extends Enumeration { val User = Value("User") }
scala> val a = E.withName("User")
a: E.Value = User
Note that when using withName, if the string does not match any name in the enumeration you get an exception.
Then always use the enumeration fields in your comparisons:
scala> a == E.User
res9: Boolean = true
If JPA only returns a string, and there is no way around it. Then I think the best option is to either convert the value to string and match string to string, or upgrade the string to a Val and compare Val. Mixing these types will not work for comparison, unless you you implement some kind of extension to the equals method, and that is tricky.

Related

Bidirectional implicit conversion in Scala

Consider the follwing generic function:
def compare[I:Ordering,T:Ordering](i:I,t:T):Int
It should compare a value of type I with a value of type T with both of them assumed to have Ordering defined. The comparison should work if there is either a way to implicitly convert I to T, or T to I. Obviously, if one uses types I and T that do not have any of the two conversions, the compiler should complain.
I am tempted to write something like this:
def compare[I:Ordering,T:Ordering](i:I,t:T)(implicit c1:I=>T, c2:T=>I):Int
But this actually asks for both conversions to exist, not at least one.
Any ideas?
EDIT: Given the comments I want to make the question complete. If both implicit conversions exist, I would like to assume a priority among the types. Then use the higher priority implicit conversion for the comparison.
Wrong Answer which I wrote initially:
Of course it will ask because you are trying to compare two different ordering. T:Ordering means that there should be an Ordering[T] available in the scope. Ordering[T] is different from Ordering[I]. It is like comparing numbers and strings where both can be ordered differently but ordering together does not makes sense.
PS: Both numbers and strings can be ordered together but that means numbers & strings will represent the same datatype here and there will be only one instance of Ordering for that data type.
Better answer:
Use a wrapper class to define the converters
object Main extends App {
def compare[I: Ordering, T: Ordering](i: I, t: T)(implicit wrapper: Wrapper[I, T]): Int = {
val converter: Either[(I) => T, (T) => I] = wrapper.getConverterBasedOnPriority
val convertedValue = if(converter.isLeft){
converter.left.map(c => c(i))
} else{
converter.right.map(c => c(t))
}
// do what ever you want
1
}
val iToT: (Int => String) = i => i.toString
val tToI: (String => Int) = s => s.toInt
// implicit def iToTWrapper = new Wrapper[Int , String ](iToT, null)
implicit def tToIWrapper = new Wrapper[Int , String ](null, tToI)
compare(1, "a")
}
class Wrapper[I, T](iToT: I => T, tToI : T => I) {
def getConverterBasedOnPriority:Either[I => T, T => I] = {
// return ordering based on priority check.
// returning iToT for example sake. Do the priority check and return accordingly
Left(iToT)
}
}
If you uncomment both implicits, it will throw and error. If you comment both implicits, it will throw and error.

implement conversion parameters function with scala

I'm trying to implement something like clever parameters converter function with Scala.
Basically in my program I need to read parameters from a properties file, so obviously they are all strings and I would like then to convert each parameter in a specific type that I pass as parameter.
This is the implementation that I start coding:
def getParam[T](key : String , value : String, paramClass : T): Any = {
value match {
paramClass match {
case i if i == Int => value.trim.toInt
case b if b == Boolean => value.trim.toBoolean
case _ => value.trim
}
}
/* Exception handling is missing at the moment */
}
Usage:
val convertedInt = getParam("some.int.property.key", "10", Int)
val convertedBoolean = getParam("some.boolean.property.key", "true", Boolean)
val plainString = getParam("some.string.property.key", "value",String)
Points to note:
For my program now I need just 3 main type of type: String ,Int and Boolean,
if is possible I would like to extends to more object type
This is not clever, cause I need to explicit the matching against every possibile type to convert, I would like an more reflectional like approach
This code doesn't work, it give me compile error: "object java.lang.String is not a value" when I try to convert( actually no conversion happen because property values came as String).
Can anyone help me? I'm quite newbie in Scala and maybe I missing something
The Scala approach for a problem that you are trying to solve is context bounds. Given a type T you can require an object like ParamMeta[T], which will do all conversions for you. So you can rewrite your code to something like this:
trait ParamMeta[T] {
def apply(v: String): T
}
def getParam[T](key: String, value: String)(implicit meta: ParamMeta[T]): T =
meta(value.trim)
implicit case object IntMeta extends ParamMeta[Int] {
def apply(v: String): Int = v.toInt
}
// and so on
getParam[Int](/* ... */, "127") // = 127
There is even no need to throw exceptions! If you supply an unsupported type as getParam type argument, code will even not compile. You can rewrite signature of getParam using a syntax sugar for context bounds, T: Bound, which will require implicit value Bound[T], and you will need to use implicitly[Bound[T]] to access that values (because there will be no parameter name for it).
Also this code does not use reflection at all, because compiler searches for an implicit value ParamMeta[Int], founds it in object IntMeta and rewrites function call like getParam[Int](..., "127")(IntMeta), so it will get all required values at compile time.
If you feel that writing those case objects is too boilerplate, and you are sure that you will not need another method in these objects in future (for example, to convert T back to String), you can simplify declarations like this:
case class ParamMeta[T](f: String => T) {
def apply(s: String): T = f(s)
}
implicit val stringMeta = ParamMeta(identity)
implicit val intMeta = ParamMeta(_.toInt)
To avoid importing them every time you use getParam you can declare these implicits in a companion object of ParamMeta trait/case class, and Scala will pick them automatically.
As for original match approach, you can pass a implicit ClassTag[T] to your function, so you will be able to match classes. You do not need to create any values for ClassTag, as the compiler will pass it automatically. Here is a simple example how to do class matching:
import scala.reflect.ClassTag
import scala.reflect._
def test[T: ClassTag] = classTag[T].runtimeClass match {
case x if x == classOf[Int] => "I'm an int!"
case x if x == classOf[String] => "I'm a string!"
}
println(test[Int])
println(test[String])
However, this approach is less flexible than ParamMeta one, and ParamMeta should be preferred.

Select random Enumeration by value

So i have this Enumeration:
object MyEnum extends Enumeration {
val a = Value(0, "Valid")
val b = Value(1, "Valid")
val c = Value(2, "Not Valid")
val d = Value(3, "Not Valid")
}
This Enumeration contains 2 types: Valid and Not valid.
Is it possible to select random MyEnum by value ?
For example i want to select a random MyEnum that is Valid.
Even when Yuval answer is correct and the solution will work, using Enumeration for encoding additional property is not recommended. By using name field for encoding the type as "Valid" or "Not Valid" (or any other classification) of your Values you are loosing build in ability of Enumeration class to reconstruct the enum value instances from String via .withName method:
// result will be always `a` for "Valid" and `b` for "Not Valid"
// it will throw an NoSuchElementException for any other string
MyEnum.withName(someString)
To put it simple when you are simply misusing name field for classification of your enum value instances. If this is not a problem in your code you're a lucky guy, but anyway document the misuse very precisely.
Enumeration was intended to express simple values, which have an index and can have nice human readable name. That's it, no more, no less.
Rule of thumb is, that when you have an enum which needs another fields it is better to model that using sealed hierarchy of case objects or case classes. Something like that (just an example for your particular case):
// Hierarchy is sealed, thus you will get can pattern match with check for exhaustiveness
sealed trait MyEnum {
def id: Int
def name: String
def valid: Boolean
}
// Helper case class, which allows to not define
private case class Value(id: Int, name: String, valid: Boolean) extends MyEnum
object MyEnum {
// Your values
val A: MyEnum = Value(0, "My A", true)
val B: MyEnum = Value(1, "My B", true)
val C: MyEnum = Value(0, "My A", false)
val D: MyEnum = Value(1, "My B", false)
// Re-implementation of methods contained in Enumeration, pick what you need
val values: Seq[MyEnum] = Seq(A, B, C, D)
// You can implement your own semantics, e.g. no Exception rather Option
def withName(name: String): Option[MyEnum] = values.find( _.name == name )
// Methods already for your example
val valids: Seq[MyEnum] = values.filter( _.valid )
// Or with randomization build in to enum
def randomValid: MyEnum = valids(util.Random.nextInt(valids.length))
}
In addition you will get more type safety:
// More type safety, fn simple does not compile
def fn(value: MyEnum) = value match {
case MyEnum.A => true
}
<console>:14: warning: match may not be exhaustive.
It would fail on the following input: Value(_, _, _)
def fn(value: MyEnum) = value match {
^
fn: (value: MyEnum)Boolean
Your milage may vary, because possibilities for writing an enum in Scala are almost endless :) In my example I'm showing only one of many possible implementations. If you have many values above mentioned approach can be impractical.
To know more about drawbacks of using Enumeration see "Scala Enumerations" blog post. Beware that solution in the end of article is not silver bullet and has problems with pattern matching exhaustiveness checking as Enumeration does.
Enjoy
This should do what you need:
val valids = MyEnum.values.filter { _.toString.equalsIgnoreCase("Valid") }.toSeq
val selected = valids(util.Random.nextInt(valids.length))
println(s"Selected $selected with id: ${selected.id}")
You can use scala Random:
scala.util.Random.shuffle(MyEnum.values.toList).find(_.toString.equalsIgnoreCase("Valid"))
and it will return an Option

HList/KList from class values

I want to be able to create a class/trait that behaves somewhat like an enumeration (HEnum in the first snippet below). I can't use a plain enumeration because each enum value could have a different type (though the container class will be the same): Key[A]. I'd like to be able to construct the enum roughly like this:
class Key[A](val name: String)
object A extends HEnum {
val a = new Key[String]("a")
val b = new Key[Int]("b")
val c = new Key[Float]("c")
}
And then I'd like to be able to perform more or less basic HList operations like:
A.find[String] // returns the first element with type Key[String]
A.find("b") // returns the first element with name "b", type should (hopefully) be Key[Int]
So far I've been playing with an HList as the underlying data structure, but constructing one with the proper type has proven difficult. My most successful attempt looks like this:
class Key[A](val name: String)
object Key {
def apply[A, L <: HList](name: String, l: L): (Key[A], Key[A] :: L) = {
val key = new Key[A](name)
(key, key :: l)
}
}
object A {
val (a, akeys) = Key[String, HNil]("a", HNil)
val (b, bkeys) = Key[Int, Key[String] :: HList]("b", akeys)
val (c, ckeys) = Key[Float, Key[Int] :: HList]("c", bkeys)
val values = ckeys // use this for lookups, etc
def find[A]: Key[A] = values.select[A]
def find[A](name: String): Key[A] = ...
}
The problem here is that the interface is clunky. Adding a new value anywhere besides the end of the list of values is error prone and no matter what, you have to manually update values any time a new value is introduced. My solution without HList involved a List[Key[_]] and error prone/unsafe casting to the proper type when needed.
EDIT
I should also mention that the enum example found here is not particularly helpful to me (although, if that can be adapted, then great). The added compiler checks for exhaustive pattern matches are nice (and I would ultimately want that) but this enum still only allows a homogeneous collection of enum values.

Understand how to use apply and unapply

I'm trying to get a better understanding of the correct usage of apply and unapply methods.
Considering an object that we want to serialize and deserialize, is this a correct usage (i.e. the Scala way) of using apply and unapply?
case class Foo
object Foo {
apply(json: JValue): Foo = json.extract[Foo]
unapply(f: Foo): JValue = //process to json
}
Firstly, apply and unapply are not necessarily opposites of each other. Indeed, if you define one on a class/object, you don't have to define the other.
apply
apply is probably the easier to explain. Essentially, when you treat your object like a function, apply is the method that is called, so, Scala turns:
obj(a, b, c) to obj.apply(a, b, c).
unapply
unapply is a bit more complicated. It is used in Scala's pattern matching mechanism and its most common use I've seen is in Extractor Objects.
For example, here's a toy extractor object:
object Foo {
def unapply(x : Int) : Option[String] =
if(x == 0) Some("Hello, World") else None
}
So now, if you use this is in a pattern match like so:
myInt match {
case Foo(str) => println(str)
}
Let's suppose myInt = 0. Then what happens? In this case Foo.unapply(0) gets called, and as you can see, will return Some("Hello, World"). The contents of the Option will get assigned to str so in the end, the above pattern match will print out "Hello, world".
But what if myInt = 1? Then Foo.unapply(1) returns None so the corresponding expression for that pattern does not get called.
In the case of assignments, like val Foo(str) = x this is syntactic sugar for:
val str : String = Foo.unapply(x) match {
case Some(s) => s
case None => throw new scala.MatchError(x)
}
The apply method is like a constructor which takes arguments and creates an object, whereas the unapply takes an object and tries to give back the arguments.
A simple example:
object Foo {
def apply(name: String, suffix: String) = name + "." + suffix
def unapply(name: String): Option[(String, String)] = {
//simple argument extractor
val parts = name.split("\\.")
if (parts.length == 2) Some(parts(0), parts(1)) else None
}
}
when you call
val file = Foo("test", "txt")
It actually calls Foo.apply("test", "txt") and returns test.txt
If you want to deconstruct, call
val Foo(name) = file
This essentially invokes val name = Foo.unapply(file).get and returns (test, txt) (normally use pattern matching instead)
You can also directly unpack the tuple with 2 variables, i.e.
scala> val Foo(name, suffix) = file
val name: String = test
val suffix: String = txt
BTW, the return type of unapply is Option by convention.
So apply and unapply are just defs that have extra syntax support.
Apply takes arguments and by convention will return a value related to the object's name. If we take Scala's case classes as "correct" usage then the object Foo's apply will construct a Foo instance without needing to add "new". You are free of course to make apply do whatever you wish (key to value in Map, set contains value in Set, and indexing in Seq come to mind).
Unapply, if returning an Option or Boolean can be used in match{} and pattern matching. Like apply it's just a def so can do whatever you dream up but the common usage is to extract value(s) from instances of the object's companion class.
From the libraries I've worked with serialization/deserialization defs tend to get named explicitly. E.g., write/read, show/read, toX/fromX, etc.
If you want to use apply/unapply for this purpose the only thing I'd suggest is changing to
def unapply(f: Foo): Option[JValue]
Then you could do something like:
val myFoo = Foo("""{name: "Whiskers", age: 7}""".asJson)
// use myFoo
val Foo(jval) = myFoo
// use jval