Given
case class Foo (
x: Int = 1,
y: String,
)
What is the best way to instantiate said class, overwriting default params only if a local condition is fulfilled (e.g. the local variable corresponding to the constructor parameter is not None)
object Test {
/* Let's pretend I cannot know the state of x,y
* because they come from the network, a file... */
val x: Option[Int] = getX()
val y: Option[String] = getY()
Foo(
x=???, // how to get x=if(x.isDefined) x else "use default of Foo" here
y=y.get,
)
}
The straightforward solution of checking the condition and instantiating the case class differently does not scale (seven arguments with default values -> 128 different instantiations)
Solution 1) One solution I know is:
object Foo {
val Defaults = Foo(y="")
}
object Test {
val x: Option[Int] = getX()
val y: Option[String] = getY()
Foo(
x=x.getOrElse(Foo.Defaults.x)
y=y.get
)
}
This works ok-ish. When y is None I get the NoSuchElementException, but that's OK because it is a mandatory constructor argument. However, this is clearly a hack and has the distinct drawback that it is possible to write:
Foo(
x=x.getOrElse(Foo.Defaults.x)
y=y.getOrElse(Foo.Defaults.y)
)
When y is None you get a non-sensical default value for y.
Solution 2) Another solution is something like:
sealed trait Field
case object X extends Field
object Foo {
private val template = Foo(y="")
val Defaults = {
case X => template.x
}
}
object Test {
val x: Option[Int] = getX()
val y: Option[String] = getY()
Foo(
x=x.getOrElse(Foo.Defaults(X))
y=y.get
)
}
This is a bit better type safety-wise, but now I have to create a type for each default parameter.
What would a correct and concise solution look like?
Not clear to me how you can do better than the following:
object Foo { def apply(optX: Option[Int], optY: Option[String]): Foo = Foo(optX.getOrElse(1), optY.get) }
case class Foo private(x: Int, y: String)
Foo(Some(5), None) // fails with NoSuchElementException
Foo(Some(5), Some("Hi")) // succeeds in creating Foo(5, "Hi")
Foo(None, Some("Hi")) // succeeds in creating Foo(1, "Hi"), note the default value of x
Whether a parameter is required or optional with a default is encoded via the invocation of get for the former and getOrElse for the latter.
Of course, you could wrap the Option's get method to provide a more meaningful message when required parameters are omitted.
I realize that is not far from your solution 1 and may not meet your needs.
Related
I am getting an error in the extractor step (unapply method call).
The error message is: Wrong number of arguments for the extractors. found 2; expected 0
Can someone please help what is causing the error (where my misunderstanding is).
class ABC(val name:String, val age:Int) //class is defined.
object ABC{
def apply(age:Int, name:String) = new ABC(name, age)
def unapply(x:ABC) = (x.name, x.age)
}
val ins = ABC(25, "Joe") //here apply method is in action.
val ABC(x,y) = ins //unapply is indirectly called. As per my understanding , 25 and Joe suppose to be captured in x and y respectively. But this steps gives error.
The error I get is
an unapply result must have a member def isEmpty: Boolean
The easiest way to fix this is to make unapply return an Option:
def unapply(x: ABC) = Option((x.name, x.age))
The unapply method in an extractor which binds values must return an Option. This is because there's no intrinsic guarantee that an extractor will always succeed. For instance consider this massively oversimplified example of an extractor for an email address:
object Email {
def unapply(s: String): Option[(String, String)] =
s.indexOf('#') match {
case idx if idx >= 0 =>
val (user, maybeSite) = s.splitAt(idx)
if (maybeSite.length < 2 || maybeSite.lastIndexOf('#') > 0) None
else Some(user -> maybeSite.tail)
case _ => None
}
}
At the application site:
val Email(u, s) = "user3103957#stackoverflow.example.xyz"
Turns into code that's basically (from the description in Programming In Scala (Odersky, Spoon, Venners (3rd ed))):
val _tmpTuple2 =
"user3103957#stackoverflow.example.xyz" match {
case str: String =>
Email.unapply(str).getOrElse(throw ???)
case _ => throw ???
}
val u = _tmpTuple2._1
val s = _tmpTuple2._2
Technically, since the compiler already knows that the value is a String, the type check is elided, but I've included the type check for generality. The desugaring of extractors in a pattern match also need not throw except for the last extractor attempt.
I am trying to write the following implicit:
implicit class ExtractOrElse[K, V](o: Option[Map[K, V]]) {
def extractOrElse(key: K)(f: => V): V = { if (o.isDefined) o.get(key) else f }
}
Which I want to use in this way:
normalizationContexts.extractOrElse(shardId)(defaultNormalizationContext)
to avoid a clunkier syntax (normalizationContexts is an Option[Map[String, NormzalitionContext]]).
Also, let me add that it is intentional that there is only one default value: it will be used if the Option isEmpty, but if the Option isDefined, then the behavior of the Map is not changed, and it will throw an exception if the key is not found - so the default value won't be used in that case, and this is all intentional.
However, I get an error when passing in None in unit tests:
assertEquals(None.extractOrElse('a')(0), 0)
results in:
Error:(165, 37) type mismatch;
found : Char('a')
required: K
assertEquals(None.extractOrElse('a')(0), 0)
I realize that None is not parametric, as it is defined as:
case object None extends Option[Nothing] {
def isEmpty = true
def get = throw new NoSuchElementException("None.get")
What is the best way to make this work?
Instead of None.extractOrElse(...), try Option.empty[Map[Char, Int]].extractOrElse(...).
If you always use the same types for your test cases, you could also create a type alias in the specs class in order to reduce the clutter:
type OpMap = Option[Map[Char, Int]]
// ...
assertEquals(Option.empty[OpMap].extractOrElse('a')(0), 0)
Just in case, you can use flatMap and getOrElse to achieve the same thing without writing a new method:
val n = Option.empty[Map[String, Int]]
val s = Some(Map("x" → 1, "y" → 2))
n.flatMap(_.get("x")).getOrElse(3) // 3
s.flatMap(_.get("x")).getOrElse(3) // 1
s.flatMap(_.get("z")).getOrElse(3) // 3
The type system doesn't have enough information about the types K and V. There is no way to know what the type of A would be in the case where your None was Some[A].
When I create an example with explicit types, the code works as expected:
// Like this
val e = new ExtractOrElse(Option.empty[Map[Char, Int]])
e.extractOrElse('a')(0) // Equals 0
// Or like this
val e = new ExtractOrElse[Char, Int](None)
println(e.extractOrElse('a')(0))
// Or like this
val m: Option[Map[Char, Int]] = None
val e = new ExtractOrElse(m)
println(e.extractOrElse('a')(0))
I'm trying to create a Map of objects like this:
case class Worker(var name:String, var tasks:List[Int]=List())
val map = Map[Int, Worker]().withDefaultValue(Worker(""))
So that whenever a new request id (the key in the map) comes in I can create a new worker object for that id. I do not know how many request ids will come in or the range of id beforehand. But this code gives a very weird result.
scala> map.size
res34: Int = 0
scala> map(1).name = "first worker"
map(1).name: String = first worker
scala> map.size
res35: Int = 0
scala> map(3).name = "request #3"
map(3).name: String = request #3
scala> map.size
res36: Int = 0
scala> map(1).name
res37: String = request #3
It is not the result I expected. The map size is always 0. There is no new Worker objects created. I tried with mutable and immutable Maps, case class and regular class with new Worker and also tried { override def default(key:Int)=Worker("") }. Nothing works as expected. Can someone help me understand the behavior of withDefaultValue or override def default? or scala way to do this? I know I can achieve this the java way if map.containsKey(xx) map.get(xx).name = "abc" else map.put(xx, new Worker). But it is tedious.
Accessing the default value of a Map does not add that value to the Map itself as a new value. You are simply mutating the same default value each time (same reference). It would be quite unexpected for a standard Map implementation to mutate itself.
To implement what you want, you might actually want to override the apply method to add a new element to the Map when one is not found.
import scala.collection.mutable.HashMap
case class Worker(var name: String, var tasks: List[Int] = Nil)
class RequestMap extends HashMap[Int, Worker] { self =>
override def apply(a: Int): Worker = {
super.get(a) match { // Find the element through the parent implementation
case Some(value) => value // If it exists, return it
case None => {
val w = Worker("") // Otherwise make a new one
self += a -> w // Add it to the Map with the desired key
w // And return it
}
}
}
}
This does what you want, but it is just an example of a direction you can take. It is not thread safe, and would need some sort of locking mechanism to make it so.
scala> val map = new RequestMap
map: RequestMap = Map()
scala> map(1)
res3: Worker = Worker(,List())
scala> map(2)
res4: Worker = Worker(,List())
scala> map.size
res5: Int = 2
Say I have the following data structure:
case class Timestamped[CC[M] < Seq[M]](elems : CC, timestamp : String)
So it's essentially a sequence with an attribute -- a timestamp -- attached to it. This works fine and I could create new instances with the syntax
val t = Timestamped(Seq(1,2,3,4),"2014-02-25")
t.elems.head // 1
t.timestamp // "2014-05-25"
The syntax is unwieldly and instead I want to be able to do something like:
Timestamped(1,2,3,4)("2014-02-25")
t.head // 1
t.timestamp // "2014-05-25"
Where timestamped is just an extension of a Seq and it's implementation SeqLike, with a single attribute val timestamp : String.
This seems easy to do; just use a Seq with a mixin TimestampMixin { val timestamp : String }. But I can't figure out how to create the constructor. My question is: how do I create a constructor in the companion object, that creates a sequence with an extra member value? The signature is as follows:
object Timestamped {
def apply(elems: M*)(timestamp : String) : Seq[M] with TimestampMixin = ???
}
You'll see that it's not straightforward; collections use Builders to instantiate themselves, so I can't simply call the constructor an override some vals.
Scala collections are very complicated structures when it comes down to it. Extending Seq requires implementing apply, length, and iterator methods. In the end, you'll probably end up duplicating existing code for List, Set, or something else. You'll also probably have to worry about CanBuildFroms for your collection, which in the end I don't think is worth it if you just want to add a field.
Instead, consider an implicit conversion from your Timestamped type to Seq.
case class Timestamped[A](elems: Seq[A])(timestamp: String)
object Timestamped {
implicit def toSeq[A](ts: Timestamped[A]): Seq[A] = ts.elems
}
Now, whenever I try to call a method from Seq, the compiler will implicitly convert Timestamped to Seq, and we can proceed as normal.
scala> val ts = Timestamped(List(1,2,3,4))("1/2/34")
ts: Timestamped[Int] = Timestamped(List(1, 2, 3, 4))
scala> ts.filter(_ > 2)
res18: Seq[Int] = List(3, 4)
There is one major drawback here, and it's that we're now stuck with Seq after performing operations on the original Timestamped.
Go the other way... extend Seq, it only has 3 abstract members:
case class Stamped[T](elems: Seq[T], stamp: Long) extends Seq[T] {
override def apply(i: Int) = elems.apply(i)
override def iterator = elems.iterator
override def length = elems.length
}
val x = Stamped(List(10,20,30), 15L)
println(x.head) // 10
println(x.timeStamp) // 15
println(x.map { _ * 10}) // List(100, 200, 300)
println(x.filter { _ > 20}) // List(30)
Keep in mind, this only works as long as Seq is specific enough for your use cases, if you later find you need more complex collection behavior this may become untenable.
EDIT: Added a version closer to the signature you were trying to create. Not sure if this helps you any more:
case class Stamped[T](elems: T*)(stamp: Long) extends Seq[T] {
def timeStamp = stamp
override def apply(i: Int) = elems.apply(i)
override def iterator = elems.iterator
override def length = elems.length
}
val x = Stamped(10,20,30)(15L)
println(x.head) // 10
println(x.timeStamp) // 15
println(x.map { _ * 10}) // List(100, 200, 300)
println(x.filter { _ > 20}) // List(30)
Where elems would end up being a generically created WrappedArray.
Is there a way to get the Type of a field with scala reflection?
Let's see the standard reflection example:
scala> class C { val x = 2; var y = 3 }
defined class C
scala> val m = ru.runtimeMirror(getClass.getClassLoader)
m: scala.reflect.runtime.universe.Mirror = JavaMirror ...
scala> val im = m.reflect(new C)
im: scala.reflect.runtime.universe.InstanceMirror = instance mirror for C#5f0c8ac1
scala> val fieldX = ru.typeOf[C].declaration(ru.newTermName("x")).asTerm.accessed.asTerm
fieldX: scala.reflect.runtime.universe.TermSymbol = value x
scala> val fmX = im.reflectField(fieldX)
fmX: scala.reflect.runtime.universe.FieldMirror = field mirror for C.x (bound to C#5f0c8ac1)
scala> fmX.get
res0: Any = 2
Is there a way to do something like
val test: Int = fmX.get
That means can I "cast" the result of a reflection get to the actual type of the field? And otherwise: is it possible to do a reflection set from a string? In the example something like
fmx.set("10")
Thanks for hints!
Here's the deal... the type is not known at compile time, so, basically, you have to tell the compiler what the type it's supposed to be. You can do it safely or not, like this:
val test: Int = fmX.get.asInstanceOf[Int]
val test: Int = fmX.get match {
case n: Int => n
case _ => 0 // or however you want to handle the exception
}
Note that, since you declared test to be Int, you have to assign an Int to it. And even if you kept test as Any, at some point you have to pick a type for it, and it is always going to be something static -- as in, in the source code.
The second case just uses pattern matching to ensure you have the right type.
I'm not sure I understand what you mean by the second case.