amnaredo / test

0 stars 0 forks source link

Dynamic reading by composite Reader of tagged case classes #98

Open amnaredo opened 3 years ago

amnaredo commented 3 years ago

Hi,

imagine you have a set of 10 sealed/tagged case classes, their instances are persisted and you want to dynamically read them (you don't know which one of them you currently read) :

@key("A") case class A(i: Int) extends SealedTrait
@key("A") case class B(s: String) extends SealedTrait

Possible solution is to introduce a composite Reader with a partial function that pattern matches over key and reads particular types :

case key if key == A.getClass.getSimpleName => read[A](str)
case key if key == B.getClass.getSimpleName => read[B](str)

It would all work nicely, except it is not quite easy to construct such a Polymorhpic composite Reader that would be applied by scalac prior to Reader[A] or Reader[B] to match the tagged string ["A", {"i": 1}] or ["A", {"s": "x"}] and applied the partial function:

Reader[(String, ???)]  // this seems to be a dead end cause ??? type varies

Another solution is to persist and read these 2 pieces of information (key,obj) separately which is not very cool and maintainable at all...

Do you have any idea how this might be done by uPickle ? I have a feeling there is a fancy solution ...

ID: 49 Original Author: l15k4

amnaredo commented 3 years ago

If you make them all inherit from the same sealed trait, read[SealedTrait](str) will do exactly what you want. This is not always possible, but if it is, that's the best way forward. If you can't make them all inherit from the same sealed trait, writing a wrapper:

sealed trait SealedTrait
case class WrapA(a: A) extends SealedTrait
case class WrapB(a: B) extends SealedTrait
...

... does what you want, and effectively performs the tagging that you seem to want to do manually.

Lastly, if you want to do this two-stage reading yourself, then you'll have to do this two-stage writing yourself too. e.g. you'll write("tag", write(myObj)) and then

val (tag, data) = read[(String, String)](str)
val res = tag match{
 ... read[A](data)
}

Does this satisfy your requirements?

Original Author: lihaoyi

amnaredo commented 3 years ago

Yes, fantastic, two stage writing + (String, String) Reader, thank you a lot !

Original Author: l15k4