two type parameters with the same name

前端 未结 4 744
野趣味
野趣味 2021-01-02 23:44

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, do

相关标签:
4条回答
  • 2021-01-03 00:10

    Its not just related to scala. In most languages you can do that. Because as you said the variables are in different scope.

    In c#

    class test
    {
          int i;
          void method(int i)
          {
              this.i = i;
          }
    }
    

    this represents the self type. I am not sure about the this functionality in scala. But the reason for your question is scope level.

    0 讨论(0)
  • 2021-01-03 00:11

    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)
                              ^    
    
    0 讨论(0)
  • 2021-01-03 00:17

    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.

    0 讨论(0)
  • 2021-01-03 00:33

    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.

    0 讨论(0)
提交回复
热议问题