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]]
Related
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.
I am trying to model (in my Scala application) a list of options presented in my web page and am scratching my head coming up with a solution for mapping a String value posted from the client to it's corresponding object in the backend.
eg. Let's say it is a list of Animals and the user can choose 1 which gets posted to the backend.
Animals
Polar Bear
Rabbit
Great White Shark
When a request comes in, I want to convert the Great White Shark String to an Animal but not sure on how best to match the
String to the appropriate type in the backend.
So far I have this.
sealed abstract class Animal(val name: String)
case object GreatWhite extends Animal("Great White Shark")
case object PolarBear extends Animal("Polar Bear")
Which allows me to do this to match the String from the UI to it's corresponding case object in my Scala application.
def matcher(animal: String) = animal match {
case GreatWhite.name => GreatWhite
case PolarBear.name => PolarBear
}
Problem
If the List of Animal's grows long however, this matcher is going to be very cumbersome since I need to have a case expression for every Animal.
I would much appreciate any experienced Scala guys giving me a pointer on a more elegant solution.
It's looks like what you need is simply have a hash table of String to Animal.
Such approach gives you ability to get result in constant time O(1) even with extensivly growing list.
val mapping = Map[String, Animal]("Rabbit" -> Rabbit, "Polar Bear" -> PolarBear /* ... */ )
// matcher
mapping.get(animal)
UPD.
Some useful comments below.
sealed abstract class Animal(val name: String)
case object GreatWhite extends Animal("Great White Shark")
case object PolarBear extends Animal("Polar Bear")
val mapping: Map[String, Animal] = Seq(GreatWhite, PolarBear).map(x => x.name -> x).toMap
mapping
Have you looked at Enums? If they are usable for you, Enums have a .withName method http://yefremov.net/blog/scala-enum-by-name/
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!
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".
How do i define generic parameter "on the fly"?
Example:
I have some method def get[T](name: String)
Simple case class
case class User(name: String, password: String, age: Option[Int])
Then i get all my case accessors
def getMethods[T: TypeTag] = typeOf[T].decls.sorted.collect {
case m: MethodSymbol if m.isCaseAccessor => m
}.toList
val caseAccessors = getMethods[User]
And i need to call get method with every accessor method and parametrize it by accessorMethod return type
For example:
caseAccessors.map{accessorMehtod => get[accessorMehtod.returnType](accessorMehtod.name)}
Is there any way to do it?
As it said in the comments to your question - it's not possible to extract type from runtime-reflection of object in compile-time - so you're loosing all typechecks (and possible have to do asInstanceOf somewhere).
But, depending on your needs - you could choose some typesafe alternative, like using Shapeless Records, which could also allow you to access fields by name, but in typesafe way. So, there will be just HList[String :: String :: Option[Int]] (which stores all field types inside) instead of List[Method].
You can also easely convert record into the case class: User(userRecord("name"), userRecord("passsword"),userRecord("age"). But it's all requires that possible field names ("name", "password", etc.) should be known at compile time.