Usually rewriting Scala class classes

Is it possible to replace arguments in the case class at all? More specifically, let's say I need a substitute function that gets the case "find" class and the case "replace" class (both the left and right sides of the grammar rule) and the target class, and the function returns a new case class with case find class arguments replaced by a replacement case class? A function can also just take the case (Product?) Class and a function that will be applied to all arguments / products of the case class.

Obviously, for a particular case class, I could use unapply and apply - but what is the best / easiest way / etc in the general case (considering any case class) to write such a function?

I am wondering if there is a good solution using the reflection functions of Scala 2.10 or Iso.hlist from formless.

For example, what I really want to do is give classes like the following ...

class Op[T] case class From(x:Op[Int]) extends Op[Int] case class To(x:Op[Int]) extends Op[Int] case class Target(a:Op[Int], b:Op[Int]) extends ... // and lots of other similar case classes 

... there is a function that can take an arbitrary case class and return a copy of it with any elements of type From being replaced by instances of type To.

+4
source share
3 answers

If you pardon the plugin, I think you will find that the rewrite component of our Kiama language processing library is ideal for this kind of purpose. It provides a very powerful form of strategic programming.

Here is a complete solution that rewrites To in From in a tree created from instances of the case class.

 import org.kiama.rewriting.Rewriter class Op[T] case class Leaf (i : Int) extends Op[Int] case class From (x : Op[Int]) extends Op[Int] case class To (x : Op[Int]) extends Op[Int] case class Target1 (a : Op[Int], b : Op[Int]) extends Op[Int] case class Target2 (c : Op[Int]) extends Op[Int] object Main extends Rewriter { def main (args : Array[String]) { val replaceFromsWithTos = everywhere { rule { case From (x) => To (x) } } val t1 = Target1 (From (Leaf (1)), To (Leaf (2))) val t2 = Target2 (Target1 (From (Leaf (3)), Target2 (From (Leaf (4))))) println (rewrite (replaceFromsWithTos) (t1)) println (rewrite (replaceFromsWithTos) (t2)) } } 

Output signal

 Target1(To(Leaf(1)),To(Leaf(2))) Target2(Target1(To(Leaf(3)),Target2(To(Leaf(4))))) 

The idea behind the replaceFromsWithTos value is that the rule construct removes the partial function in order to be able to work with any type of value. In this case, the partial function is defined only in the From nodes, replacing them with To nodes. The everywhere combinator says: "Apply my argument to all the nodes in the tree, leaving the same places where the argument is not applied.

Much more can be done than this simple rewrite. See Kiama rewriting core documentation for details, including links to a few examples.

+5
source

I don't think that you really will find a better way than just using unapply / apply through pattern matching:

 someValue match { case FindCaseClass(a, b, c) => ReplaceCaseClass(a, b, c) // . . . } 

You have to write rules to associate FindCaseClass with ReplaceCaseClass some way, and although you could do it a little more succinctly just by using names, this has the added benefit of also checking the number and type of fields of the case class at compile time to make sure that everything matches only the right one.

There may be a way to do this automatically, using the fact that all case classes extend Product , but the fact that productElement(n) return Any can make it a little pain - I think where the thought was supposed to happen. Here is something to get you started:

 case class From(i: Int, s: String, xs: Seq[Nothing]) case class To(i: Int, s: String, xs: Seq[Nothing]) val iter = From(5,"x",Nil).productIterator val f = To.curried iter.foldLeft(f: Any) { _.asInstanceOf[Any => Any](_) } // res0: Any = To(5,x,List()) 

But actually, I think you're better off with a model version.

Edit: Here is a version with relevant code reorganized into a method:

 case class From(i: Int, s: String, xs: Seq[Nothing]) case class To(i: Int, s: String, xs: Seq[Nothing]) type Curryable = { def curried: _ => _ } def recase(from: Product, to: Curryable) = { val iter = from.productIterator val f = to.curried iter.foldLeft(f: Any) { _.asInstanceOf[Any => Any](_) } } recase(From(5,"x",Nil), To) // res0: Any = To(5,x,List()) 
+4
source

I experimented a bit with formless and was able to come up with the following, relatively general way of converting one case class to another:

 import shapeless._ /* shapeless 1.2.3-SNAPSHOT */ case class From(s: String, i: Int) case class To(s: String, i: Int) implicit def fromIso = Iso.hlist(From.apply _, From.unapply _) implicit def toIso = Iso.hlist(To.apply _, To.unapply _) implicit def convert[A, B, L <: HList] (a: A) (implicit srcIso: Iso[A, L], dstIso: Iso[B, L]) : B = dstIso.from(srcIso.to(a)) val f1 = From("Hi", 7) val t1 = convert(f1)(fromIso, toIso) println("f1 = " + f1) // From("Hi", 7) println("t1 = " + t1) // To("Hi", 7) 

However, I could not get the right to participate. Perfectly,

 val t1: To = f1 

would be enough or maybe

 val t1 = convert(f1) 

Another nice improvement is to get rid of the need to explicitly declare iso-implicits ( fromIso , toIso ) for each case class.

+4
source

Source: https://habr.com/ru/post/1446286/


All Articles