Assume that we have a class constructor that takes parameters that have default value.
class A(val p1 : Int = 3, val p2 : Int = 4)
Let's say I don't have control over this class and can't modify it in anyway. What I want to do is to call A's constructor with p1 = 5, p2 = (if condition1 == true then 5 else default value). One way to do this is
if(condition1)
x = new A(5,5)
else
x = new A(5)
As you can see, this can easily get big if there are many parameters and each must be supplied conditionally. What I want is something like
x = new A(p1 = 5, p2 = <if condition1 = true then 5 else default>)
How can I do that? Note that the fields in class A are vals, so I cant change them after instantiating A.
It seems to me you have three possibilities:
Create variables to hold each of the values you want to specify, do all the code to fill in the values, and instantiate A once at the end. This requires knowing the default values, as Ionut mentioned. I don't think creating a throwaway object to read the defaults is all that hackish -- certainly not as much as embedding the defaults themseves -- but whatever.
Use the reflection API to create A. I'm not exactly sure how to do that but almost certainly you can pass in a list of parameters, with any unspecified parameters defaulted. This requires Scala 2.10; before that, only the Java reflection API was available and you'd have to hack through the internal implementation of optional parameters, which is hackish.
Use macros. Also 2.10+. I think that quasiquotes should make it possible to do this without too much difficulty, although I'm not too familiar with them so I can't say for sure.
Fetch the default values,
val defaultA = new A()
Then
val x = new A(p1 = 5, p2 = if (cond) 5 else defaultA.p2)
Here's the quick and sort of wrong answer:
x = new A(p1 = 5, if (condition1) 5 else A.$lessinit$greater$default$2)
The "wrong" part is that this works in 2.10 but not in 2.9. The magick name for the default method has changed from version to version (notably, between 2.9 and 2.10), so it's safer to look up its name and call it via reflection. See Instantiating a case class with default args via reflection, How do I access default parameter values via Scala reflection? and Scala dynamic instantiation with default arguments.
What about having a derived class from A like this:
class D(val t2: (Boolean, Int)) extends A {
override val p2: Int = if(t2._1) t2._2 else A.init$default$2
}
Now you can instantiate A like this:
x = new D((condition1, 5))
If you have more parameters, then you can add a similar override statement and tuple parameter for each.
I can't help feeling what you are asking for is a bit unreasonable. As stated already it should be possible with macros. But generally if you are not satisfied with the provided constructor / factory, then you have to write you're own wrapper / factory. Personally I think that we may want to look a fresh at the whole issue of default values, Null objects and the Option class. However it is possible to cut out boilerplate, without macros. Put a an implicit Boolean in your utility package:
implicit class BooleanRich2(n : Boolean) {
def apply[T](v1: T, v2: T): T = if (n) v1 else v2
}
Then say we wish to use the following class that we can't modify:
final class A(val p1: Int = 1, val p2: Int = 2, val p3: Int = 3, val p4: Int = 4){
override def toString = s"p1: $p1, p2: $p2, p3: $p3, p4: $p4"
}
We can use it as follows:
var c1 = true
var c2 = false
def c3(s: String) = (s =="")
val a = new A(c1(20, 1)) //p1: 20, p2: 2, p3: 3, p4: 4
println(a) //p1: 20, p2: 2, p3: 3, p4: 4
val b = new A(p3 = c2(20, 3), p4 = c3("")(20, 4))
println(b) //p1: 1, p2: 2, p3: 3, p4: 20
val c = new A(p3 = c1(20, 3), p4 = c3("A non empty String")(20, 4))
println(c) //p1: 1, p2: 2, p3: 20, p4: 4
Related
Lets say i have a data structure that holds a parameterised type of data:
case class Terminal[A](value: A, name: String ="")
I can easily create a Terminal[Double] if i pass it a materialised constant:
val terminal = Terminal(2.0)
However, i want it to also be able to receive a (not materialised) input so that i can evaluate the terminal multiple times with different contexts. I can achieve a simple solution by calling value by name, i.e.
class Terminal[A](value: => A, name: String ="") {
def getValue = this.value
}
var x = 1.0
val terminal = new Terminal(x)
terminal.getValue // 1.0
x = 100.0
terminal.getValue // 100.0
However the user of this program would have to initialise the input with something like var input_x = None, which is not nice, and then change its state, which in turn would have to make me turn value into a Option[A]
Is this the best way to deal with this situation? Isn't any design pattern or scala feature that i could use?
i can also create a class Input to represent these context-dependent inputs, but then i would need to change a lot of things.
You can use immutable objects as below:
scala> case class Terminal[A](value: A, name: String ="") {
| def update(newValue: A): Terminal[A] = this.copy(value = newValue)
| def getValue: A = this.value
| }
defined class Terminal
scala> val terminal = Terminal(1.0)
terminal: Terminal[Double] = Terminal(1.0,)
scala> val updatedTerminal = terminal.update(100.0)
updatedTerminal: Terminal[Double] = Terminal(100.0,)
scala> val oldValue = terminal.getValue
oldValue: Double = 1.0
scala> val newValue = updatedTerminal.getValue
newValue: Double = 100.0
The getValue method is actually redundant here because getters come free with case classes. I just had it in there to demonstrate the example.
scala> oldValue == terminal.value
res0: Boolean = true
scala> newValue == updatedTerminal.value
res1: Boolean = true
In general, prefer case-classes if you want to create objects that don't have mutable state (For example, all singleton components are better-off as non-case classes).
This is a Scala-specific question.
Assume that you have a function (which you cannot modify) of several inputs, e.g.:
def test(x1: Int, x2:Int, x3: Int, x4: Int, x5: Int) = {
//Some logic
}
And assume that you have all the inputs for that function in a single array, e.g.:
val inputs = Array(1, 2, 3, 4, 5)
Is there a way to call test with that single array of inputs without explicitly inserting individual array elements (as in test(inputs(0),inputs(1),inputs(2),inputs(3),inputs(4)))?
This is particularly important for the case when I don't know the number of inputs and the number of elements in the array in advance (but know that they match).
No that's not possible. It's possible to use an array for a function that expects varargs by using :_* syntax. Also, your question is contradictory:
the case when I don't know the number of inputs and the number of
elements in the array in advance (but know that they match)
How could you not know the number of inputs or elements but know they match?
You can curry the function and then use one of the solutions proposed here.
For instance, using this technique:
class Acc[T](f: Function1[T, _]) {
private[this] var ff: Any = f
def apply(t: T): this.type = {
ff = ff.asInstanceOf[Function1[T,_]](t)
this
}
def get = ff match {
case _: Function1[_,_] => sys.error("not enough arguments")
case res => res.asInstanceOf[T]
}
}
def test(x1: Int, x2:Int, x3: Int, x4: Int, x5: Int) = {
//Some logic
}
val inputs = Array(1, 2, 3, 4, 5)
inputs.foldLeft(new Acc((test _).curried))((acc, i) => acc(i)).get
Not extremely safe, but it should work
You can use Java reflection (or Scala reflection, but Java's one is sufficient for this). The below is a quick and dirty solution, which assumes you have a class Test which contains exactly one test method:
val m = classOf[Test].
getMethods.find(_.getName == "test").
getOrElse(throw new Exception("No method called test"))
// inputs must be Array[Object], not Array[Int] as in your example
m.invoke(instanceOfTest, inputs)
This is rarely a good idea, however.
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.
Given the following Scala 2.9.2 code:
Updated with non-working example
import collection.immutable.SortedSet
case class Bar(s: String)
trait Foo {
val stuff: SortedSet[String]
def makeBars(bs: Map[String, String])
= stuff.map(k => Bar(bs.getOrElse(k, "-"))).toList
}
case class Bazz(rawStuff: List[String]) extends Foo {
val stuff = SortedSet(rawStuff: _*)
}
// test it out....
val b = Bazz(List("A","B","C"))
b.makeBars(Map("A"->"1","B"->"2","C"->"3"))
// List[Bar] = List(Bar(1), Bar(2), Bar(3))
// Looks good?
// Make a really big list not in order. This is why we pass it to a SortedSet...
val data = Stream.continually(util.Random.shuffle(List("A","B","C","D","E","F"))).take(100).toList
val b2 = Bazz(data.flatten)
// And how about a sparse map...?
val bs = util.Random.shuffle(Map("A" -> "1", "B" -> "2", "E" -> "5").toList).toMap
b2.makeBars(bs)
// res24: List[Bar] = List(Bar(1), Bar(2), Bar(-), Bar(5))
I've discovered that, in some cases, the makeBars method of classes extending Foo does not return a sorted List. In fact, the list ordering does not reflect the ordering of the SortedSet
What am I missing about the above code where Scala will not always map a SortedSet to a List with elements ordered by the SortedSet ordering?
You're being surprised by implicit resolution.
The map method requires a CanBuildFrom instance that's compatible with the target collection type (in simple cases, identical to the source collection type) and the mapper function's return type.
In the particular case of SortedSet, its implicit CanBuildFrom requires that an Ordering[A] (where A is the return type of the mapper function) be available. When your map function returns something that the compiler already knows how to find an Ordering for, you're good:
scala> val ss = collection.immutable.SortedSet(10,9,8,7,6,5,4,3,2,1)
ss: scala.collection.immutable.SortedSet[Int] = TreeSet(1, 2, 3, 4, 5,
6, 7, 8, 9, 10)
scala> val result1 = ss.map(_ * 2)
result1: scala.collection.immutable.SortedSet[Int] = TreeSet(2, 4, 6, 8, 10,
12, 14, 16, 18, 20)
// still sorted because Ordering[Int] is readily available
scala> val result2 = ss.map(_ + " is a number")
result2: scala.collection.immutable.SortedSet[String] = TreeSet(1 is a number,
10 is a number,
2 is a number,
3 is a number,
4 is a number,
5 is a number,
6 is a number,
7 is a number,
8 is a number,
9 is a number)
// The default Ordering[String] is an "asciibetical" sort,
// so 10 comes between 1 and 2. :)
However, when your mapper function turns out to return a type for which no Ordering is known, the implicit on SortedSet doesn't match (specifically, no value can be found for its implicit parameter), so the compiler looks "upward" for a compatible CanBuildFrom and finds the generic one from Set.
scala> case class Foo(i: Int)
defined class Foo
scala> val result3 = ss.map(Foo(_))
result3: scala.collection.immutable.Set[Foo] = Set(Foo(10), Foo(4), Foo(6), Foo(7), Foo(1), Foo(3), Foo(5), Foo(8), Foo(9), Foo(2))
// The default Set is a hash set, therefore ordering is not preserved
Of course, you can get around this by simply supplying an instance of Ordering[Foo] that does whatever you expect:
scala> implicit val fooIsOrdered: Ordering[Foo] = Ordering.by(_.i)
fooIsOrdered: Ordering[Foo] = scala.math.Ordering$$anon$9#7512dbf2
scala> val result4 = ss.map(Foo(_))
result4: scala.collection.immutable.SortedSet[Foo] = TreeSet(Foo(1), Foo(2),
Foo(3), Foo(4), Foo(5),
Foo(6), Foo(7), Foo(8),
Foo(9), Foo(10))
// And we're back!
Finally, note that toy examples often don't exhibit the problem, because the Scala collection library has special implementations for small (n <= 6) Sets and Maps.
You're probably making assumption about what SortedSet does from Java. You need to specify what order you want the elements to be in. See http://www.scala-lang.org/docu/files/collections-api/collections_8.html