I have an enumeration of string coming from the result of a query into a database.
In other words, i'm querying a events from a database, and one of the attribute is event description, which should always belong to a set of well known string. E.g.
"PreferedLabelAdded"
"PreferedLabelChanged"
And so on.
I would like to model the possible string that comes from the database within Scala. I was thinking about using an Enum but I don't see how with what i have seen online.
Ultimately, what i would like to do is to compare the value of the attribute to one of this value to perform some tasks according to the type of event descriptions.
Alghough i could go and simply enter my magic string as such
if (eventDesc == "PreferedLabelAdded")
I find it bad, because one does not get one point of contact to change those string, but instead it get spread all over the code.
Maybe simply using an Object as such
Object EventDesc {
val PrefAdded = "PreferedLabelAdded""
val PrefChanged = "...."
}
If anyone has some specific suggestion with that, that would be great.
Daniel
You can extend the scala.Enumeration class to create your enumeration:
object EventDesc extends Enumeration {
type EventDesc = Value
val PreferedLabelAdded, PreferedLabelChanged, UNKNOWN = Value
}
val eventDesc = EventDesc.withName("PreferedLabelAdded")
if (eventDesc == EventDesc.PreferedLabelChanged) {
Console.println(eventDesc)
}
You can also do pattern matching:
eventDesc match {
case PreferedLabelAdded | PreferedLabelChanged => handleEvent(context)
case UNKNOWN => ignoreEvent(context)
case _ => // noop
}
Related
A use case here is to imagine a config object, that is a collection of key-value pairs.
I would like a to create a type representing this collection of key, value pairs. And I'd also like to have the values be of varied types. Further, I'd like to keep those key, value pairs in a single collection, despite the values being of different types. But I don't want to allow the values to by of any type at all. Finally, I'd like to be able to recover the value for a given key. (Bonus points, I'd like to ensure keys are unique)
Something like:
trait Property
case class SProperty(name: String, value:String) extends Property
case class DProperty(name: String, value:Double) extends Property
case class IProperty(name: String, value:Int) extends Property
case class Properties(props: List[Property]) {
def getValueByName(name: String) = {
props.find(p => p.name == name).map(p => p.value)
}
}
And this works ok, but it creates an ugly API. For example:
val properties = Properties(
List(
Property("some name", "some value"),
Property("another name", "another value")
)
)
If I have a test that checks:
properties.getValueByName("some name").getOrElse(None) shouldBe "some value"
the test will fail with
SProperty("some value") is not equal to "some value"
I considered using a shapeless HList, however that would allow Properties to take on any type. I only want Property types in the collection, and I want the values in those to be restricted to types String | Double | Int.
Is there a better way to define this collection of key, value pairs that allows for a specific set of value types and that can provide me with a .get method?
Is there a better way to define my getValueByName method such that it returns the underlying value and not the wrapped value?
One approach is to write the getter like this:
def getValueByName[T](name: String): Option[Any] = {
Properties.find(p => p.name == name).map {
case Property(_, SProperty(v)) => Some(v)
case Property(_, DProperty(v)) => Some(v)
case Property(_, IProperty(v)) => Some(v)
case _ => None
}
}
But this starts to be a lot of places to modify the code to add a new type allowed for the property values. Returning an Option[Any] seems wrong. And to use the getter, you'd need to know the type of the value ahead of time. That may be good, but it may be a hassle.
I was also unable to find an acceptable approach using typeclasses, but I admit that I'm not deeply experienced with writing typeclasses.
I am reading this example from their docs:
class Email(val username: String, val domainName: String)
object Email {
def fromString(emailString: String): Option[Email] = {
emailString.split('#') match {
case Array(a, b) => Some(new Email(a, b))
case _ => None
}
}
}
println(Email.fromString("scala.center#epfl.ch"))
val scalaCenterEmail = Email.fromString("scala.center#epfl.ch")
scalaCenterEmail match {
case Some(email) => println(
s"""Registered an email
|Username: ${email.username}
|Domain name: ${email.domainName}
""")
case None => println("Error: could not parse email")
}
My questions:
What is Some and Option?
What is a factory method (just some function that creates a new object and returns it?)
What is the point of companion objects? Is it just to contain functions that are available to all instances of class? Are they like class methods in Ruby?
What is Some and Option?
Option is a data structure that represents optionality, as the name suggests. Whenever a computation may not return a value, you can return an Option. Option has two cases (represented as two subclasses): Some or None.
In the example above, the method Email.fromString can fail and not return a value. This is represented with Option. In order to know whether the computation yielded a value or not, you can use match and check whether it was a Some or a None:
Email.fromString("scala.center#epfl.ch") match {
case Some(email) => // do something if it's a Some
case None => // do something it it's a None
}
This is much better than returning null because now whoever calls the method can't possibly forget to check the return value.
For example compare this:
def willReturnNull(s: String): String = null
willReturnNull("foo").length() // NullPointerException!
with this
def willReturnNone(s: String): Option[String] = None
willReturnNone("foo").length() // doesn't compile, because 'length' is not a member of `Option`
Also, note that using match is just a way of working with Option. Further discussion would involve using map, flatMap, getOrElse or similar methods defined on Option, but I feel it would be off-topic here.
What is a factory method (just some function that creates a new object and returns it?)
This is nothing specific to Scala. A "factory method" is usually a static method that constructs the value of some type, possibly hiding the details of the type itself. In this case fromString is a factory method because it allows you create an Email without calling the Email constructor with new Email(...)
What is the point of companion objects? Is it just to contain functions that are available to all instances of class? Are they like class methods in Ruby?
As a first approximation, yes. Scala doesn't have static members of a class. Instead, you can have an object associated with that class where you define everything that is static.
E.g. in Java you would have:
public class Email {
public String username;
public String domain;
public static Optional<Email> fromString(String: s) {
// ...
}
}
Where as in Scala you would define the same class as roughly:
class Email(val username: String, val domain: String)
object Email {
def fromString(s: String): Option[Email] = {
// ...
}
}
I would like to add some examples/information to the third question.
If you use akka in companion object you can put every message that you use in case method (it should proceed and use by actor). Moreover, you can add some val for a name of actors or other constant values.
If you work with JSON you should create a format for it (sometimes custom reads and writes). This format you should put inside companion object. Methods to create instances too.
If you go deeper to Scala you can find case classes. So a possibility to create an object of this class without new is because there is a method apply in "default" companion object.
But in general, it's a place where you can put every "static" method etc.
About Option, it provides you a possibility to avoid some exception and make something when you don't have any values.
Gabriele put an example with email, so I'll add another one.
You have a method that sends email, but you take email from User class. The user can have this field empty, so if we have something like it
val maybeEmail: Option[String] = user.email you can use for example map to send an email
maybeEmail.map(email => sendEmail(email))
So if you use it, during writing methods like above you don't need to think that user specify his email or not :)
val a = user.type match {
case Member => doSomething(if(user.location.isDefined) user.location.get.name else "")
}
I want to safely access the user.location which is a Option[Location] if it exists, otherwise just use an empty string.
case class Location(id: Int, name: String)
Is this possible?
Simply:
doSomething(user.location.map(_.name).getOrElse(""))
Best way to "deep match" into an Option that is nested inside some other object is through a structural pattern match, finally giving a variable name to the desired value and using it in the right side of =>.
I don't know what your "Member" class looks like, but assume that it has two parameters, since we don't care about the first one we put an underscore there "_" and then refer directly to the Location class in the second param, like this:
val a = user match {
case Member(_, Location(_, Some(name))) => doSomething(name)
}
What about this?
val a = user.type match {
case Member(id, type) if(user.location.isDefined) => doSomething(user.location.get.name)
}
If your Member is a case class with attributes id, type for example.
I am trying to build a wrapper around data saved in a redis database.
What I want to do is access it as normal as possible as if I have a scala value of some type.
It works, except for comparing it to anything (with == or similar).
So I want to be able to compare it to objects of the inner type.
Best I'll give example code first. This is a rough abstraction of my wrapper:
case class RedisObjectValue[T](name: String, default: T) {
def :=(value: T): Boolean = {
redisMagic.set(name, value)
}
def get: T = {
redisMagic.get[T](name).getOrElse(default)
}
override def equals(o: Any) = o match {
case ov: RedisObjectValue[T] =>
if (this.hashCode() == ov.hashCode()) true // Same reference
else this.get == ov.get
case v =>
// If we don't compare to another RedisObjectValue, let the equals method of data type handle it
this.get.equals(v)
}
}
I have an implicit conversion set up in the companion object so that I can use the wrapper wherever I would use the base type.
object RedisObjectValue {
implicit def objectValueToValue[T](ov: RedisObjectValue[T]): T = {
ov.get
}
}
So, as I said, everything works.
Except the comparing stuff. For that, let's say I have a class Player two values, userId and name.
class Player {
val userId = RedisObjectValue("userId", 0)
val name = RedisObjectValue("player", "Unknown")
}
And now I have a list of players and want to filter to get all players with name "Unknown".
list.filter(_.name == "Unknown")
Which does not work. If I extend the filter filter call and write a function, it tells me in IntelliJ "Comparing unrelated types".
And yeah, I understand what it is telling me, but I want to solve that. I mean I can easily compare Long to Int and similar stuff, so there must be a way to make them comparable, right?
In the code above I have even written the equals function which uses the comparison to the inner type, but it seems like it is not used.
Of course, I can always call .get on the values, like players.filter(_.name.get == "Unknown"), but that's a bit dirty and I would love to avoid that.
EDIT: Found the real problem after some analysis
Old text still above for reading, I will explain the problem here now.
I have a snippet which shows what is not working: https://ideone.com/AmQrkH
The Problem: The RedisObjectValue is defined as Long. When I am comparing it now with
players.filter(_.userId == 2)
for example, it doesn't give any results, even if there are players with userId 2, or to be more exact: 2l.
For direct Long it's not a problem.
players.filter(_._id == 2)
is working.
So is there any fix for that, so that comparable instances of classes to T can also be compared to RedisObjectValue[T], and not just T itself?
Replace this.get.equals(v) with get == v. (Removing this is unrelated, just a good idea in general, the main thing is to use == instead of equals).
Consider this:
scala> 2 equals 2L
res79: Boolean = false
scala> 2 == 2L
res80: Boolean = true
How to get object from Play cache (scala)
Code to set:
play.api.cache.Cache.set("mykey98", new Product(98), 0)
Code to get:
val product1: Option[Any] = play.api.cache.Cache.get("mykey98")
I get Option object. How to get actual Product object I stored in first step.
First and foremost, I would suggest using Cache.getAs, which takes a type parameter. That way you won't be stuck with Option[Any]. There are a few ways you can do this. In my example, I'll use String, but it will work the same with any other class. My preferred way is by pattern matching:
import play.api.cache.Cache
Cache.set("mykey", "cached string", 0)
val myString:String = Cache.getAs[String]("mykey") match {
case Some(string) => string
case None => SomeOtherClass.getNewString() // or other code to handle an expired key
}
This example is a bit over-simplified for pattern matching, but I think its a nicer method when needing to branch code based on the existence of a key. You could also use Cache.getOrElse:
val myString:String = Cache.getOrElse[String]("mykey") {
SomeOtherClass.getNewString()
}
In your specific case, replace String with Product, then change the code to handle what will happen if the key does not exist (such as setting a default key).