Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Does Inheritance in implicit value classes introduce an overhead?

I want to apply scala's value classes to one of my projects because they enable me to enrich certain primitive types without great overhead (I hope) and stay type-safe.

object Position {

  implicit class Pos( val i: Int ) extends AnyVal with Ordered[Pos] {

    def +( p: Pos ): Pos = i + p.i

    def -( p: Pos ): Pos = if ( i - p.i < 0 ) 0 else i - p.i

    def compare( p: Pos ): Int = i - p.i

  }
}

My question: Will the inheritance of Ordered force the allocation of Pos objects whenever I use them (thereby introduce great overhead) or not? If so: Is there a way to circumvent this?

like image 220
peri4n Avatar asked May 14 '13 09:05

peri4n


People also ask

What is an implicit class in Scala?

An implicit class is a class marked with the implicit keyword. This keyword makes the class's primary constructor available for implicit conversions when the class is in scope. Implicit classes were proposed in SIP-13.

What is value class?

At a fundamental level: value classes define objects which, once created, never change their value. A variable of a value type may only be changed by re-assigning to that variable. When we wish to only modify some portion of a value class (one attribute, say), we are compelled to reassign the whole object.

What are value classes in Java?

A value class declaration introduces a class whose instances are value objects. An identity object is a class instance or array that does have identity—the traditional behavior of objects in Java. An identity object can mutate its non- final fields and is associated with a synchronization monitor.

What is a value class in Scala?

Value classes are a new mechanism which help to avoid allocating run time objects. AnyVal define value classes. Value classes are predefined, they coincide to the primitive kind of Java-like languages. There are nine predefined value types : Double, Float, Long, Int, Short, Byte, Char, Unit, and Boolean.


1 Answers

Everytime Pos will be treated as an Ordered[Pos], allocation will happen. There are several cases when allocation has to happen, see http://docs.scala-lang.org/overviews/core/value-classes.html#when_allocation_is_necessary.

So when doing something as simple as calling <, you will get allocations:

val x = Pos( 1 )
val y = Pos( 2 )
x < y // x & y promoted to an actual instance (allocation)

The relevant rules are (quoted from the above article):

Whenever a value class is treated as another type, including a universal trait, an instance of the actual value class must be instantiated and: Another instance of this rule is when a value class is used as a type argument.

Disassembling the above code snippet confirms this:

 0: aload_0
 1: iconst_1
 2: invokevirtual #21                 // Method Pos:(I)I
 5: istore_1
 6: aload_0
 7: iconst_2
 8: invokevirtual #21                 // Method Pos:(I)I
11: istore_2
12: new           #23                 // class test/Position$Pos
15: dup
16: iload_1
17: invokespecial #26                 // Method test/Position$Pos."<init>":(I)V
20: new           #23                 // class test/Position$Pos
23: dup
24: iload_2
25: invokespecial #26                 // Method test/Position$Pos."<init>":(I)V
28: invokeinterface #32,  2           // InterfaceMethod scala/math/Ordered.$less:(Ljava/lang/Object;)Z

As can be seen we do have two instances of the "new" opcode for class Position$Pos

UPDATE: to avoid the allocation in simples cases like this, you can manually override each method (even if they only forward to the originlal implementation):

override def <  (that: Pos): Boolean = super.<(that)
override def >  (that: Pos): Boolean = super.>(that)
override def <= (that: Pos): Boolean = super.<=(that)
override def >= (that: Pos): Boolean = super.>=(that)

This will remove the allocation when doing x < y by example. However, this still leaves the cases when Pos is treated as an Ordered[Pos] (as when passed to a method taking a Ordered[Pos] or an Ordered[T] with T being a type parameter). In this particular case, you will still get an allocation and there no way around that.

like image 122
Régis Jean-Gilles Avatar answered Sep 26 '22 02:09

Régis Jean-Gilles