Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Scala Modifiers and Type parametrization

Tags:

scala

I'm creating a memoization class.

Each class memoizes a function type and has the following definition:

 class MemoizedFunction1[-T1, +R](f: T1 => R) {
    private[this] val cache = mutable.Map[T1, R]()
    def apply(t: T1): R = cache.getOrElseUpdate(t,f(t))
  }

This compiles nicely and works as expected. However, if I remove the modified private[this] I get the following error:

contravariant type T1 occurs in invariant position in type => scala.collection.mutable.Map[T1,R] of value cache

Why is that, when I remove the modifier, suddenly the contravariant type T1 interferes with the invariant type of the Map? How do modifiers affect type parametrization?

like image 878
JaimeJorge Avatar asked Apr 06 '12 23:04

JaimeJorge


People also ask

What is a type parameter Scala?

Language. Methods in Scala can be parameterized by type as well as value. The syntax is similar to that of generic classes. Type parameters are enclosed in square brackets, while value parameters are enclosed in parentheses.

What does => mean in Scala?

=> is syntactic sugar for creating instances of functions. Recall that every function in scala is an instance of a class. For example, the type Int => String , is equivalent to the type Function1[Int,String] i.e. a function that takes an argument of type Int and returns a String .

How do you find the type of a variable in Scala?

Use the getClass Method in Scala The getClass method in Scala is used to get the class of the Scala object. We can use this method to get the type of a variable.

Can I pass parameter to class in Scala?

Singleton Objects in Scala We can call it's methods directly, we can't pass parameters to its primary constructor.


3 Answers

Let's assume you can remove [this].

Without [this] you can add method getOtherCache:

class MemoizedFunction1[-T1, +R](f: T1 => R) { 
  private val cache = mutable.Map[T1, R]() // trait Map[A, B] extends Iterable[(A, B)] with Map[A, B] with MapLike[A, B, Map[A, B]]
  def apply(t: T1): R = cache.getOrElseUpdate(t,f(t))

  def getOtherCache(other: MemoizedFunction1[T1, R]) {
    val otherCache: mutable.Map[T1, R] = other.cache;
  }
}

class A
class B extends A

val mf1: MemoizedFunction1[B, B] = new MemoizedFunction1[B, B](b => b)

val mf2: MemoizedFunction1[B, B] = new MemoizedFunction1[A, B](a => new B)
// mf2 is MemoizedFunction1[B, B]
// mf2 contains mutable.Map[A, B]

mf1.getOtherCache(mf2) //Error! mf2.cache is NOT mutable.Map[B, B]!
like image 146
senia Avatar answered Oct 20 '22 23:10

senia


Not that I understand all of it, but this is addressed in section 4.5 (Variance Annotations) of the Scala Language Specification 2.9 on page 45

References to the type parameters in object-private or object-protected values, variables, or methods (§5.2) of the class are not checked for their variance position. In these members the type parameter may appear anywhere without restricting its legal variance annotations.

To simplify your example, according to the spec, this is fine:

class Inv[T]

class Foo[-T] {
  private[this]   val a: Inv[T] = sys.error("compiles")
  protected[this] val b: Inv[T] = sys.error("compiles")
}

But if you remove [this] it will complain. At some level it makes sense since if it is not object private or protected the contravariant return type could leak outside the object and cause a runtime error.

like image 29
huynhjl Avatar answered Oct 20 '22 23:10

huynhjl


Programming in Scala touches on this topic in Section 19.7 Object private data: "object private members can be accessed only from within the object in which they are defined. It turns out that accesses to variables from the same object in which they are defined do not cause problems with variance."

like image 36
michid Avatar answered Oct 20 '22 21:10

michid