In Scala, how can I tell an abstract base class that the parameter type T supports implicit conversion from Int (or Float or ...)?

I find it difficult to migrate from the C ++ / Templates world to scala. I am used to the fact that I can use any operation on the T parameter of the template that I want, as long as everything that I use to create an instance of T supports these operations (mainly in Duck compilation mode). I can not find the corresponding idiom in Scala, which will allow me to define an abstract class with one type of parameter and which expects a specific interface for type T.

That almost works for me, but I can’t understand how to tell the abstract class (Texture [T <: Summable [T]]) that T supports conversion / construction from Int. How can I add an implicit conversion to the Summable dash so that Texture knows that T supports the conversion?

trait Summable[T] { def += (v : T) : Unit def -= (v : T) : Unit } object Int4 { implicit def int2Int4(i : Int) = new Int4(i, i, i, i) } class Int4 (var x : Int, var y : Int, var z : Int, var w : Int) extends Summable[Int4] { def this (v : Int) = this(v, v, v, v) def += (v : Int4) : Unit = { x += vx; y += vy; z += vz; w += vw } def -= (v : Int4) : Unit = { x -= vx; y -= vy; z -= vz; w -= vw } } abstract class Texture[Texel <: Summable[Texel]] { var counter : Texel def accumulate(v : Texel) : Unit = { counter += v } def decrement() : Unit = { counter -= 1 } //< COMPILE ERROR HERE, fails to find implicit } class Int4Target extends Texture[Int4] { var counter : Int4 = new Int4(0, 1, 2, 3) } 
+4
source share
3 answers

You can define an implicit constructor like this

 abstract class Texture[Texel <: Summable[Texel]](implicit int2Texel: Int => Texel) { //... 

This essentially tells the compiler that there must be an implicit conversion function available from Int to Texel to build the Texture instance. Assuming you have a function defined somewhere in scope (what you are doing), you should no longer receive a compilation error.

Edit2: Ok I initially read your code incorrectly, you really only need one implicit parameter from Int => Texel . Your code compiles for me with the above modification.

Edit: you really need two conversion functions: one from Texel => Int and the other from Int => Texel to properly reassign var

+4
source

The fundamental difference between C ++ templates and everything in Scala is that C ++ templates are compiled for each use - that is, if you use a template with int and double , then two different classes are compiled, and they only compile, when some code really uses it.

Scala, on the other hand, has a separate compilation. Not as good as Java, given the limitations of the JVM, but still following a basic principle. So, if someone has a type parameter, it still compiles when declared, and only one such class ever exists. This compiled code should support all possible parameters than you can call with it, which leads to quite different limitations than templates.

As for traits and implicit conversions, traits do not support parameters, and implicit conversions (presentation boundaries) are parameters. Use a class instead.

+3
source

In scala, it is not possible to require that an implicit conversion exist for the attribute parameter type. There is a good reason for this. Suppose we defined a as:

 trait ATrait[T <% Int] { def method(v: T) { println(v: Int) } } 

And then he made copies of it in two places:

 package place1 { implicit def strToInt(s: String) = 5 val inst = new ATrait[String] } package place2 { implicit def strToInt(s: String) = 6 val inst = new ATrait[String] } 

And then they used these instances, for example:

 val a = if (someTest) place1 else place2 a.method("Hello") 

Should print 5 or 6 ? That is, what implicit conversion should be used? Implicits must be found at compile time, but you don’t know what kind of implicit conversion the object was present.

In other words, implications are provided by the area in which they are used, and not by the objects in which they are used; the latter would be impossible.

So, about your problem. Instead of using implicit, you can use regular Member:

 trait Summable[T] { def -= (v: T): Unit def -= (v: Int) { this -= (encode(v)) } def encode(i: Int): T } class Int4 (var x: Int, var y: Int, var z: Int, var w: Int) extends Summable[Int4] { def -= (v : Int4) : Unit = { x -= vx; y -= vy; z -= vz; w -= vw } def encode(i: Int) = Int4.int2Int4(i) } 

Now the decrement method compiles correctly.

Another way of saying this, do not think about implications, since properties belonging to the type (ie, "can be implicitly converted from Int" are not INT4 properties). These are values ​​that can be identified using types.

Hope this helps.

+2
source

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


All Articles