Instantiating a case class from a list of parameters - scala

Given:
case class Foo(a: Int, b: String, c: Double)
you can say:
val params = Foo(1, "bar", 3.14).productIterator.toList
and get:
params: List[Any] = List(1, bar, 3.14)
Is there a way to "go backwards" and recreate a Foo object directly from this list, i.e.:
Foo.createFromList(params) // hypothetical
instead of writing:
Foo(params(0).asInstanceOf[Int], params(1).asInstanceOf[String], params(2).asInstanceOf[Double])
EDIT: it seems that it boils down to being able to send the elements of a list as parameters to a function without writing them out explicitly, e.g.:
def bar(a: Int, b: Int, c: Int) = //...
val list = List(1, 2, 3, 4, 5)
bar(list.take(3)) // hypothetical, instead of:
bar(list(0), list(1), list(2))
I would sort of expect to be able to do:
bar(list.take(3): _*)
but that doesn't seem to work.
EDIT: Solution based on extempore's answer, but invoking the constructor directly instead of using the apply method:
case class Foo(a: Int = 0, b: String = "bar", c: Double = 3.14) {
val cs = this.getClass.getConstructors
def createFromList(params: List[Any]) =
cs(0).newInstance(params map { _.asInstanceOf[AnyRef] } : _*).asInstanceOf[Foo]
}
Now you can do:
scala> Foo().createFromList(List(4, "foo", 9.81))
res13: Foo = Foo(4,foo,9.81)
You can also refactor the creation method into a trait:
trait Creatable[T <: Creatable[T]] {
val cs = this.getClass.getConstructors
def createFromList(params: List[Any]) =
cs(0).newInstance(params map { _.asInstanceOf[AnyRef] } : _*).asInstanceOf[T]
}
case class Bar(a: Int = 0, b: String = "bar", c: Double = 3.14) extends Creatable[Bar]
And do e.g.:
scala> val bar = Bar()
bar: Bar = Bar(0,bar,3.14)
scala> bar == bar.createFromList(bar.productIterator.toList)
res11: Boolean = true

scala> case class Foo(a: Int, b: String, c: Double)
defined class Foo
scala> val params = Foo(1, "bar", 3.14).productIterator.toList
params: List[Any] = List(1, bar, 3.14)
scala> Foo.getClass.getMethods.find(x => x.getName == "apply" && x.isBridge).get.invoke(Foo, params map (_.asInstanceOf[AnyRef]): _*).asInstanceOf[Foo]
res0: Foo = Foo(1,bar,3.14)
scala> Foo(1, "bar", 3.14) == res0
res1: Boolean = true
Edit: by the way, the syntax so far only being danced around for supplying the tuple as an argument is:
scala> case class Foo(a: Int, b: String, c: Double)
defined class Foo
scala> Foo.tupled((1, "bar", 3.14))
res0: Foo = Foo(1,bar,3.14)

You could use pattern matching like:
params match {
case List(x:Int, y:String, d:Double) => Foo(x,y,d)
}

Well, you can certainly do this with a tuple:
(Foo _).tupled apply (1, bar, 3.14)
But there is no real way to get from a List[S] to (A, B, C) for A, B, C <: S. There may be a way of doing this with HLists of course

Another one liner using case class companion object curried method and completely ignoring type safety :)
scala> case class Foo(a: Int, b: String, c: Double)
defined class Foo
scala> val lst = List(1, "bar", 3.14)
lst: List[Any] = List(1, bar, 3.14)
scala> val foo = lst.foldLeft(Foo.curried: Any){case (r, v) => r.asInstanceOf[Function[Any, _]](v) }.asInstanceOf[Foo]
foo: Foo = Foo(1,bar,3.14)

Related

Scala: passing function parameters as tuple

Let's say I have:
def foo(i: Int, s: String)
and have:
val tuple: (Int, String) = (1, "s")
can I pass tuple to foo without adding a wrapper for foo?
Yes, its possible. Using .tupled one can convert the lambda into accepting the tuple as argument.
Scala REPL
scala> def foo(i: Int, s: String): Int = i
foo: (i: Int, s: String)Int
scala> (foo _).tupled
res3: ((Int, String)) => Int = scala.Function2$$Lambda$226/234698513#45984654
scala> val tuple: (Int, String) = (1, "s")
tuple: (Int, String) = (1,s)
scala> (foo _).tupled(tuple)
res5: Int = 1
foo(tuple._1, tuple._2) should work.
If you want something more maintainable, I would recommend following:
type MyTuple = (Int, String)
def foo(t:MyTuple) = ??? // some code
val tuple = (1, "s")
foo(tuple) // works
Also inside foo, the best way to unwrap the tuple would be
val (int, string) = t

Scala collect items of a type from a collection

Consider an Array[Any]
val a = Array(1,2,"a")
a: Array[Any] = Array(1, 2, a)
We can collect all the items of type Int like this,
a.collect { case v: Int => v }
res: Array[Int] = Array(1, 2)
Though how to define a function that collects items of a given type, having unsuccessfully tried this,
def co[T](a: Array[Any]) = a.collect { case v: T => v }
warning: abstract type pattern T is unchecked since it is eliminated by erasure
which delivers
co[Int](a)
ArraySeq(1, 2, a)
co[String](a)
ArraySeq(1, 2, a)
You need to provide a ClassTag for the pattern match to actually work:
import scala.reflect.ClassTag
def co[T: ClassTag](a: Array[Any]) = a.collect { case v: T => v }

Create an instance of case class B using common prefix members of case class A in scala

I have a case class A which has all the member variables same as those in case class B and in the same sequence. Only that B has two additional member variables which follow the common ones. Code is as follows:
object CaseClassExample {
case class A(x: Int, y: String, z: Boolean)
case class B(x: Int, y: String, z: Boolean, a: Option[Int], b: Int)
def main(args: Array[String]) {
val a = A(1, "hello", true)
val b1 = B(expand a members here, None, 2)
val b2 = B(expand a members here, Some(10), 3)
}
}
I want to create an instance of B using the values in a above, and only provide additional values. Something like this:
val b1 = B(expand a members here, None, 2)
val b2 = B(expand a members here, Some(10), 3)
How can I do it without typing too much?
Can I use Scala macros for this?
I know I can create auxiliary constructors and so on.
NOTE: My actual class A has about 18 member variables.
You can do it by using shapeless
scala> import shapeless.Tuples._
import shapeless.Tuples._
scala> import shapeless.HList._
import shapeless.HList._
scala> case class A(x: Int, y: String, z: Boolean)
defined class A
scala> case class B(x: Int, y: String, z: Boolean, a: Option[Int], b: Int)
defined class B
scala> val a = A(1, "hello", true)
a: A = A(1,hello,true)
scala> val b1 = (B.apply _).tupled((A.unapply(a).get.hlisted ::: (Some(3),5).hlisted).tupled)
b1: B = B(1,hello,true,Some(3),5)
scala> val b1 = (B.apply _).tupled((A.unapply(a).get.hlisted ::: (None,5).hlisted).tupled)
b1: B = B(1,hello,true,None,5)
Don't know if there is an built in by scala for this
You could have an A as a member of B
case class A(x: Int, y: String, z: Boolean)
case class B(anA: A, a: Option[Int], b: Int)
val a = A(1, "hello", true)
val b = B(a, None, 5)
Another option would be to write an Apply method in case class B
case class B (x: Int, y: String, z: Boolean, a: Option[Int], b: Int) {
def apply(aClass: A, newA: Option[Int], newB:Int) {
B(x = aClass.x,
y = aClass.y,
z = aClass.z,
a = newA,
b = newB
)
}
}
Then you should be able to do
val aObj = A(3,"name", true)
val bObj = B(aObj, None, 5)
Another option might be to make the differential parameters optional and merge the two types:
case class A(x: Int, y: String, z: Boolean, a: Option[Int] = None, b: Option[Int] = None) {
def toB(newA: Option[Int], newB: Int) = copy(a = newA, b = newB)
def isB: Boolean = ??? // to test whether it's an A or "B"
}

Problems with contravariance in Scala

I want to define a type-class like this:
trait CanFold[-T, R] {
def sum(acc: R, elem: T): R
def zero: R
}
implicit object CanFoldInts extends CanFold[Int, Int] {
def sum(x: Int, y: Int) = x + y
def zero = 0
}
implicit object CanFoldSeqs extends CanFold[Traversable[_], Traversable[_]] {
def sum(x: Traversable[_], y: Traversable[_]) = x ++ y
def zero = Traversable()
}
def sum[A, B](list: Traversable[A])(implicit adder: CanFold[A, B]): B =
list.foldLeft(adder.zero)((acc,e) => adder.sum(acc, e))
However, the problem is when I do this I get a Traversable[Any] and it
would be nice to get a Traversable[Int] instead:
scala> sum(List(1,2,3) :: List(4, 5) :: Nil)
res10: Traversable[Any] = List(1, 2, 3, 4, 5)
To make matters worse, I cannot define an implicit for
Traversable[Int] after defining one for Traversable[_], because then
the definitions would cause ambiguity. After pulling my hair out I
gave up.
Is there any way I could make that sum return a
Traversable[T] instead of a Traversable[Any]?
Looking at how sum() is defined on Seq in Scala's library, I can see it works with Numeric, which is invariant, but I want default implementations for supertypes and having the result be different than the input (same as the fold operation) is nice.
The only way I know to add type parameters to such type classes is to use a def instead of an object:
implicit def CanFoldSeqs[A] = new CanFold[Traversable[A], Traversable[A]] {
def sum(x: Traversable[A], y: Traversable[A]) = x ++ y
def zero = Traversable()
}
scala> sum(List(1, 2, 3) :: List(4, 5) :: Nil)
res0: Traversable[Int] = List(1, 2, 3, 4, 5)

scala tuple unpacking

I know this question has come up many times in different ways. But it is still not clear to me. Is there a way to achieve the following.
def foo(a:Int, b:Int) = {}
foo(a,b) //right way to invoke foo
foo(getParams) // is there a way to get this working without explicitly unpacking the tuple??
def getParams = {
//Some calculations
(a,b) //where a & b are Int
}
It's a two step procedure. First turn foo into a function, then call tupled on it to make it a function of a tuple.
(foo _).tupled(getParams)
#dave-griffith is dead on.
You can also call:
Function.tupled(foo _)
If you want to wander into "way more information than I asked for" territory, there are also methods built into partially applied functions (and on Function) for currying. A few input/output examples:
scala> def foo(x: Int, y: Double) = x * y
foo: (x: Int,y: Double)Double
scala> foo _
res0: (Int, Double) => Double = <function2>
scala> foo _ tupled
res1: ((Int, Double)) => Double = <function1>
scala> foo _ curried
res2: (Int) => (Double) => Double = <function1>
scala> Function.tupled(foo _)
res3: ((Int, Double)) => Double = <function1>
// Function.curried is deprecated
scala> Function.curried(foo _)
warning: there were deprecation warnings; re-run with -deprecation for details
res6: (Int) => (Double) => Double = <function1>
Wherein the curried version is invoked with multiple argument lists:
scala> val c = foo _ curried
c: (Int) => (Double) => Double = <function1>
scala> c(5)
res13: (Double) => Double = <function1>
scala> c(5)(10)
res14: Double = 50.0
Finally, you can also uncurry/untuple if needed. Function has builtins for this:
scala> val f = foo _ tupled
f: ((Int, Double)) => Double = <function1>
scala> val c = foo _ curried
c: (Int) => (Double) => Double = <function1>
scala> Function.uncurried(c)
res9: (Int, Double) => Double = <function2>
scala> Function.untupled(f)
res12: (Int, Double) => Double = <function2>
Function.tupled(foo _)(getParams) or the one suggested by Dave.
EDIT:
To respond to your comment:
What if foo happens to be the
constructor of some class?
In that case, this trick won't work.
You can write a factory method in the companion object of your class and then obtain the tupled version of its apply method using one of the aforementioned techniques.
scala> class Person(firstName: String, lastName: String) {
| override def toString = firstName + " " + lastName
| }
defined class Person
scala> object Person {
| def apply(firstName: String, lastName: String) = new Person(firstName, lastName)
| }
defined module Person
scala> (Person.apply _).tupled(("Rahul", "G"))
res17: Person = Rahul G
With case classes you get a companion object with an apply method for free, and thus this technique is more convenient to use with case classes.
scala> case class Person(firstName: String, lastName: String)
defined class Person
scala> Person.tupled(("Rahul", "G"))
res18: Person = Person(Rahul,G)
I know that's a lot of code duplication but alas... we don't have macros (yet)! ;)
I appreciate some of the other answers which were closer to what you asked for, but I found it easier for a current project to add another function which converts tuple parameters into the split parameters:
def originalFunc(a: A, b: B): C = ...
def wrapperFunc(ab: (A, B)): C = (originalFunc _).tupled(ab)
Now, you can implement foo and make it take a param of the Tuple2 class like so.
def foo(t: Tuple2[Int, Int]) = {
println("Hello " + t._1 + t._2)
"Makes no sense but ok!"
}
def getParams = {
//Some calculations
val a = 1;
val b = 2;
(a, b) //where a & b are Int
}
// So you can do this!
foo(getParams)
// With that said, you can also do this!
foo(1, 3)