Return a copy of the case class from a generic function without execution time

I want to get rid of a run-time cast to a generic ( asInstanceOf[A] ) without implicit conversions.

This happens when I have a fairly clean data model consisting of case classes with a common attribute and want to implement a general algorithm on it. As an example, the resulting algorithm should take a class of type A , which is a subclass of trait T and should return a copy of a specific class A with some updated field.

This is easy to achieve when I can simply add the abstract copy method to the base attribute and implement it in all subclasses. However, this potentially contaminates the model with methods required only by certain algorithms, and sometimes this is not possible, because the model may be beyond my control.

Here is a simplified example that demonstrates a problem and a solution that uses runtime execution.

Please do not dwell on the details.

Suppose there is a property and some case classes that I cannot change:

 trait Share { def absolute: Int } case class CommonShare( issuedOn: String, absolute: Int, percentOfCompany: Float) extends Share case class PreferredShare( issuedOn: String, absolute: Int, percentOfCompany: Float) extends Share 

And here is a simple way to recalculate the current percentOfCompany when the total number of shares has changed and the field in the case class has been updated

 def recalculateShare[A <: Share](share: A, currentTotalShares: Int): A = { def copyOfShareWith(newPercentage: Float) = { share match { case common: CommonShare => common.copy(percentOfCompany = newPercentage) case preferred: PreferredShare => preferred.copy(percentOfCompany = newPercentage) } } copyOfShareWith(share.absolute / currentTotalShares.toFloat).asInstanceOf[A] } 

Some examples of calls in REPL:

 scala> recalculateShare(CommonShare("2014-01-01", 100, 0.5f), 400) res0: CommonShare = CommonShare(2014-01-01,100,0.25) scala> recalculateShare(PreferredShare("2014-01-01", 50, 0.5f), 400) res1: PreferredShare = PreferredShare(2014-01-01,50,0.125) 

So this works, and as I understand it, calling .asInstanceOf[A] will never fail, but it is necessary to compile the code. Is there a way to avoid use at runtime without using implicit conversions?

+5
source share
1 answer

You have several options that I can think of, and it basically comes down to a balance of how the overall solution you want and how much verbosity you can endure.

asInstanceOf

Your decision seems dirty, but I donโ€™t think itโ€™s all that bad, and gnarliness is pretty well maintained.

type class

An excellent approach to ensuring data type behavior while maintaining separation of problems in your code is the Enrich Your Library / typeclass diagram. I wish I had the perfect link for this, but I do not. Take a look at these terms or an โ€œimplicit classโ€ and you can find enough examples to get a drift.

You can create trait Copyable[A] { def copy(?): A } typeclass ( implicit class ) and make instances for each of your types. The problem here is that it is somewhat verbose, especially if you want this copy method to be completely general. I left the parameter list as a question mark, because you could just adapt it to what you really need, or you could try to make it work for any case class , which would be quite difficult as far as I know.

Optics

Lenses were created to deal with this kind of awkwardness. You can check out Monocle , which is a good general approach to this problem. Although it still does not solve the problem of verbosity, it may be the way to go if you have this problem, repeated throughout your project, and especially if you find yourself trying to make changes deep into your graphic object.

+5
source

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


All Articles