Retaining type information in List[Any] - scala

I'm using certain external library that has a method which is overloaded several times with different arguments, something like:
insertInto(index: Int, int: Int)
insertInto(index: Int, lng: Long)
insertInto(index: Int, dbl: Double)
insertInto(index: Int, str: String)
And a certain case class I'm using whose data I want to pass onto said methods, say:
case class C(str: String, lng: Long, dbl: Double, int: Int /* more values */)
val c = C("asd", 1, 1.1, 1)
Right now I'm using the library method like:
insertInto(1, c.int)
insertInto(2, c.lng)
insertInto(3, c.dbl)
insertInto(4, c.str)
//more insertions...
But since I'm always using the index of the value in the case classes I figured that maybe I could could save up on some lines of code (around 10) with something like the following:
c.productIterator.zipWithIndex.toList.foreach {
case (idx, value) => insertInto(idx, value)
}
But this doesn't work because I'd be iterating a List[Any] and therefore the compiler complains that I'm not passing the correct argument type to insertInto since Any is not String, Int, Long, Double, etc..
What would be the correct way of handling this? Thanks in advance

case class A[T](t:T)(implicit tag: ClassManifest[T]){
val newTag = tag
override def toString= t+" "+tag.toString
}
case class C(xs:List[Any])
val c=C(List(A("a"),A[Long](1), A[Double](1.1), A[Int](1)))
c.xs.foreach(println)
Try this. I am using Scala 2.9.3. In newer versions, you can use TypeTag and ClassTag. Check here
So now you have the class type information. You can devise some mechanism to map class type value as string to .class instance and then use asinstanceof to cast it.

Related

Reading data into custom case classes in Doobie

Let's say I have a case class X(id: Int, name: String, age: Int) and some function (in my case, withUniqueGeneratedKeys in doobie) that returns X. If I have already defined X I am good.
But in my case the core data structure is something like:
case class XData(name: String, age: Int)
case class MaterializedX(id: Int, element: XData)
And of course I could write a line like case class X(id: Int, name: String, age: Int) to create X but it would be duplication of logic - whenever something about XData changes, I'd have to change the code for X as well. Intuitively, it feels like there should be a way to derive X from XData and MaterializedX. This transformation might mean some code, but it would save me lots of future work because I have many item types beyond X.
How could this be done? Happy to hear other approaches.
I am using Scala 3.1.2 in case this matters. Thank you!
Edit: Changed title per helpful comment, to make this question easier to understand.
I think you should be more clear about the question, I mean what title says is almost completely different from your question description (and what you might be actually looking for). Anyway, in my assumptions, what you need is probably a custom Read[MaterializedX] and Write[MaterializedX], as follows:
implicit val pointMaterializedX: Read[MaterializedX] =
Read[(Int, String, Int)]
.map {
case (id, name, age) => MaterializedX(id, XData(name, age))
}
implicit val pointWrite: Write[MaterializedX] =
Write[(Int, String, Int)]
.contramap { materializedX =>
(materializedX.id, materializedX.element.name, materializedX.element.age)
}
More documentations here
This works out of the box with doobie. Nested case classes get flattened into one row. The following code compiles without defining any custom decoders.
case class XData(name: String, age: Int)
case class MaterializedX(id: Int, element: XData)
implicitly[doobie.Read[MaterializedX]]
implicitly[doobie.Write[MaterializedX]]

apply modulus operator to Scala generic type class member?

So i have the followig class:
class MyClass[T] {
var data: T = _
def isEven: Boolean = {
if (this.data % 2 == 0) return true
false
}
}
Scala isn't allowing me to take modulus here as there is no guarantee that type "T" will be numerical.
So my question is how do i run this?
I want to allow MyClass to have any type of numeric dataTypes like Int, Float, Double etc
There's a type class called Numeric, located in package scala.math. This class provides some functionalities, which numerics (like Int, Double, ...) must have, for instance, they can be negated, or they must be able to get converted to Int, and many others, See the docs here.
So I suggest you do this:
import scala.math.Numeric
class MyClass[T : Numeric] {
var data: T = _
private final val num: Numeric[T] = implicitly
def isEven: Boolean = num.toDouble(data) % 2 == 0
}
Scala has already implemented Numeric for some types such as Int, BigInt, Char, BigDecimal, etc,. So if you use one of these standard types, you'll be just fine, but if you plan to use some type that you have defined yourself, consider implementing Numeric[YourType].
Also, try not to use var.

Can I customise the value components in a case class?

Say I have some case class in a library:
case class MyClass(a: Int, b: Int)
Later it turns out that there's a bug in my library and I need to apply some extra logic to one of these parameters to keep things working, so that from the user's perspective instances this can happen:
val x = MyClass(1, 2)
println(x.a) // prints '3' or whatever I happen to compute for 'a'
In other words, the final value for x.a is not necessarily what was passed in to the constructor. I know this looks crazy, but trust me, I need it. x.a will still return whatever was passed to the constructor in most cases, but there is one value for the constructor parameter that will lead to bugs and I need to transform it.
I see two ways to achieve this. I can make a a var:
case class MyClass(var a: Int, b: Int) {
a = someComputation()
}
but then the class becomes mutable because a can be set from the outside. My problem would be solved if I could remove or 'hide' the generated setter but it doesn't seem to be possible. If I add
private def a_=(newA: Int) {}
it doesn't override the setter generated by the var so it sees two method definitions and doesn't compile.
The second option is to create a field/method separate from the constructor parameter:
case class MyClass(private val _a: Int, b: Int) {
val a = someComputation(a)
}
but _a is used in all the special generated methods such as equals, toString, etc, whereas the custom field a doesn't feature.
Is there any way to transform the constructor parameters without affecting the rest of the API?
What I'd do, is override the apply method on the companion object to create an instance of MyClass with the right computation.
object MyClass {
def apply(a: Int, b: Int) = new MyClass(someComputation(a),b))
}
Then you can call it like val x = MyClass(1, 2), but you won't be able to call it like val x = new MyClass(1, 2) if you still want the computation to occur.
Apparently all of the above do not work outside the REPL.
Instead I'd settle on another method on the companion object, it's not as nice a solution, but it should work:
object MyClass {
def create(a: Int, b: Int) = new MyClass(someComputation(a),b))
}
So, you want something like MyClass(a=1).a == 2 to return true?
Do you really need an explanation why it is a bad idea? ;)
Thank god, it is not possible!

Syntax for accepting tuple in a function in Scala

I would like a function to consume tuple of 7 but compiler won't let me with the shown message. I failed to find a proper way how to do it. Is it even possible without explicitely typing all the type parameters like Tuple7[String,String...,String] and is it even a good idea to use Scala like this ?
def store(record:Tuple7): Unit = {
}
Error:(25, 20) class Tuple7 takes type parameters
def store(record: Tuple7): Unit = {
^
As stated by Luis you have to define what Type goes on which position for every position in the Tuple.
I`d like to add some approaches to express the same behaviour in different ways:
Tuple Syntax
For that you have two choices, what syntax to use to do so:
Tuple3[String, Int, Double]
(String, Int, Double)
Approach using Case Classes for better readability
Long tuples are hard to handle, especially when types are repeated. Scala offers a different approach for handling this. Instead of a Tuple7 you can use a case class with seven fields. The gain in this approach would be that you now can attach speaking names to each field and also the typing of each position makes more sense if a name is attached to it.
And the chance of putting values in wrong positions is reduced
(String, Int, String, Int)
// vs
case class(name: String, age: Int, taxNumber: String, numberOfChildren: Int)
using Seq with pattern matching
If your intention was to have a sequence of data seq in combination with pattern matching could also be a nice fit:
List("name", 24, "", 5 ) match {
case name:String :: age:Int ::_ :: _ :: Nil => doSomething(name, age)
}
This only works nice in a quite reduced scope. Normally you would lose a lot of type information as the List is of type Any.
You could do the following :
def store(record: (String, String, String, String, String, String, String)):Unit = {
}
which is the equivalent of :
def store(record: Tuple7[String, String, String, String, String, String, String]):Unit = {
}
You can read more about it in Programming in Scala, 2nd Edition, chapter "Next Steps in Scala", sub-chapter "Step 9. use Tuples".

bencode Scala Any type

I am porting over code that was written in Python over to Scala.
This python code [0,{}] creates a list of an integer and a dictionary.
The dictionary in this case will have keys that are strings, but the values can either be an int or a string.
I want to do something similar in Scala. I believe I would have to do something like:
List[Any] = (<int>, scala.collection.mutable.Map[String, Any](...))
I plan to bencode this list so the underlying type of 'Any' matters. How do I change the types so that the list can be bencoded?
For the most part, using Any is not the right thing to do in Scala.
Since your problem is that the map values can be either Int or String, rather than trying to use their common type (Any), make your own new type hierarchy to represent the possibilities:
sealed trait BData
case class BInt(i: Int) extends BData
case class BString(s: String) extends BData
Now your map will have a type signature of Map[String, BData]
Using BData as the "underlying type" is better than using Any because there is no ambiguity to it. It has exactly two subclasses; compared with Any, where your values might be a List[(Foo, Bar)], for all the compiler knows.
I'll take dylan answer little further.
Scala have strong type system and you should use it.
When you are using Any which is like Object in java, you lose the type.
You should define the types and let the compiler work for you:
sealed trait BData[T] {
val value: T
}
implicit class BInt(val value: Int) extends BData[Int]
implicit class BString(val value: String) extends BData[String]
the 'implicit' declaration makes the compiler "Convert" the types for you, so you can use it like this:
val bDataMap = scala.collection.mutable.Map[String, BData[_]]()
val l = List((1, bDataMap))
l(0)._2 += ("myKey1" -> "Str")
l(0)._2 += ("myKey2" -> 2)
l(0)._2("myKey1").value
and so on....