Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

two type parameters with the same name

Tags:

generics

scala

I am wondering why the two type parameters (named "A") with the same name ("A") is allowed as per the example below. I know this is a POOR naming of type parameters, don't do this.

(My guess is that they are on a on a different scope level, e.g. class level and function level, and the compiler is using some kind of name mangling)

class  MyTest[A](){
    type MyType  = A

    def checkString[A](value:A, x:MyType):A = { 
       value match {
         case x:String => println("Value is a String")
         case _ => println("Value is not a String")
       }

       x match {
          case x:String => println("x is a String")
          case _ => println("x is not a String")
       }

       value
   }
}

Example output from 2.8.0

scala> val test = new MyTest[Int]
test: MyTest[Int] = MyTest@308ff65f

scala> test.checkString("String",1)
Value is a String
x is not a String
res7: java.lang.String = String

scala> test.checkString(1,1)
Value is not a String
x is not a String
res8: Int = 1
like image 256
oluies Avatar asked Sep 20 '10 13:09

oluies


3 Answers

Nested scopes in Scala are free to shadow each others' symbol tables. Types are not the only things you can do this with. For example:

class X[A](a: A) {
  def X[A](a: A) {
    if (a==this.a) {
      val X = Some(this.a)
      X match {
        case Some(a) => "Confused much yet?"
        case _ => "Just because you can do this doesn't mean you should."
      }
    }
  }
}

The principle is that a scope has control over its namespace. This has dangers, if you use it foolishly (e.g. I have used X and a for each of three different things, and A for two--in fact, you could replace every identifier with X except for the one in the Some which has to be lower case). But it also has benefits when writing functional code--you don't have to worry about having to rename some iterating variable or type or whatever just because you happen to place it in a different context.

def example = {
  val a = Array(1,2,3,4,5)
  val sumsq = a.map(i => i*i).sum
  a.map(i => {
    val a = Array.range(1,i)
    val sumsq = a.map(i => i*i).sum  // Cut and paste from above, and works!
    sumsq + i
  }).sum
}

So be aware that you have the power to confuse yourself, and wisely choose to use that power non-confusingly.

like image 73
Rex Kerr Avatar answered Oct 13 '22 16:10

Rex Kerr


I'm not expert of Scala, but your code behave exactly what I would expected.

First, you need to know that the type parameter of method is not need bound to class.

For example, the following is valid Scala.

class Test1 {
    def test[A] (x: A) = println(x)
}

And the following is also a valid Scala code, the only different is that this one does not use the type A at all.

class Test2[A] {
    def test (x: Int) = println(x)
}

So I think it is clear now, first you created a instance of MyTest[Int], which is fine.

scala> val test = new MyTest[Int]
test: MyTest[Int] = MyTest@308ff65f

Then you called checkString[A, Int] without provide type parameter A, since it is a generic function, the compiler must inference what type is A.

scala> test.checkString("String",1)
Value is a String
x is not a String
res7: java.lang.String = String

Note:

In this time point, Scala already knows that x must be a Int and its type is fixed, since you provide it by MyTest[Int]. So the following code will yield compile error.

scala> val t = new MyTest[Int]
t: MyTest[Int] = MyTest@cb800f

scala> t.checkString ("A", "B")
<console>:8: error: type mismatch;
 found   : java.lang.String("B")
 required: t.MyType
       t.checkString ("A", "B")

Now the complier looks at the arguments you provided, and found its is

checkString ("String", 1)

which is corresponding to

checkString (value: A, x: Int)

So now compiler knows type A in checkString[A, Int] must be a string, and if you do all of this by hand, your code will look like this.

scala> val test = new MyTest[Int]
test: MyTest[Int] = MyTest@5bda13

scala> test.checkString[String]("String", 1)
Value is a String
x is not a String
res1: String = String

scala> test.checkString[Int] (3, 4)
Value is not a String
x is not a String
res4: Int = 3

scala> test.checkString[Int] ("String", 4)
<console>:8: error: type mismatch;
 found   : java.lang.String("String")
 required: Int
       test.checkString[Int] ("String", 4)
                          ^    
like image 37
Brian Hsu Avatar answered Oct 13 '22 18:10

Brian Hsu


Well I belive at scala we use same rule as in Java basically we are looking for smallest available scope in Java:

class Foo<T>{
   T instance;

   void <T> T getInstance(){
       return instance
    }
}

Will produce a compilation error since type T declared in generic method getInstance is not the same as parameter type of class Foo. In case of Scala I believe then you write

def checkString[A]

You telling compiler that function behavior will vary upon provided type but it has no connection with parameter class of outer class. Unfortunately I cannot find correct place is Scala spec right now.

like image 1
Nikolay Ivanov Avatar answered Oct 13 '22 16:10

Nikolay Ivanov