programing tip

예를 들어 (공동, 대조 및 in) 분산이 어떻게 작동합니까?

itbloger 2020. 6. 14. 11:03
반응형

예를 들어 (공동, 대조 및 in) 분산이 어떻게 작동합니까?


이 질문 에 이어 누군가 Scala에서 다음을 설명 할 수 있습니다.

class Slot[+T] (var some: T) { 
   //  DOES NOT COMPILE 
   //  "COVARIANT parameter in CONTRAVARIANT position"

}

형식 선언 +TT형식 선언 의 차이점을 이해합니다 (을 사용하면 컴파일됩니다 T). 하지만 어떻게 하나 실제로 일을 만들기에 의지하지 않고 형식 매개 변수의 공변 인 클래스 쓰기 않습니다 unparametrized를 ? 의 인스턴스로만 다음을 만들 수 있는지 어떻게 확인할 수 T있습니까?

class Slot[+T] (var some: Object){    
  def get() = { some.asInstanceOf[T] }
}

편집 -이제 이것을 아래로 가져 왔습니다.

abstract class _Slot[+T, V <: T] (var some: V) {
    def getT() = { some }
}

이것은 모두 좋지만 이제는 두 가지 유형의 매개 변수가 있습니다. 다음과 같이 질문을 다시하겠습니다.

유형 공변 인불변 Slot 클래스를 작성하려면 어떻게 해야합니까?

편집 2 : 어! 내가 사용 var하지 val. 다음은 내가 원하는 것입니다.

class Slot[+T] (val some: T) { 
}

일반적으로 공변량 유형 매개 변수는 클래스가 하위 유형화됨에 따라 범위가 변경 될 수있는 유형입니다 (또는 하위 유형에 따라 달라 지므로 "co-"접두어). 보다 구체적으로 :

trait List[+A]

List[Int]의 하위 유형 List[AnyVal]이기 때문에 Int의 하위 유형입니다 AnyVal. 이는 List[Int]유형 값 List[AnyVal]이 예상 되는 인스턴스를 제공 할 수 있음을 의미합니다 . 이것은 제네릭이 작동하는 데 매우 직관적 인 방법이지만 변경 가능한 데이터가있을 때 소리가 나지 않습니다 (유형 시스템이 깨짐). 이것이 제네릭이 Java에서 변하지 않는 이유입니다. Java 배열을 사용하는 소리가 들리지 않는 간단한 예 (공변량이 잘못됨) :

Object[] arr = new Integer[1];
arr[0] = "Hello, there!";

방금 type의 값을 type String의 배열에 할당했습니다 Integer[]. 명백한 이유 때문에 이것은 나쁜 소식입니다. Java 타입 시스템은 실제로 컴파일 타임에 이것을 허용합니다. JVM은 "유용하게" ArrayStoreException런타임에 던질 것 입니다. 스칼라의 타입 시스템은 Array클래스 의 타입 매개 변수 가 변하지 않기 때문에이 문제를 방지합니다 (선언이 [A]아닌 [+A]).

contravariance 라고하는 다른 유형의 분산이 있습니다 . 공분산으로 인해 일부 문제가 발생할 수있는 이유를 설명하므로 매우 중요합니다. 공분산은 말 그대로 공분산의 반대입니다. 매개 변수 는 하위 유형에 따라 위쪽 으로 다양 합니다. 매우 직관적 인 응용 프로그램이 있기는하지만 반 직관적이기 때문에 부분적으로 덜 일반적입니다.

trait Function1[-P, +R] {
  def apply(p: P): R
}

type 매개 변수 " - "분산 주석이 P있습니다. Function1전체적으로이 선언 은에서 P공변량 및 공변량을 의미합니다 R. 따라서 다음과 같은 공리를 도출 할 수 있습니다.

T1' <: T1
T2 <: T2'
---------------------------------------- S-Fun
Function1[T1, T2] <: Function1[T1', T2']

공지 T1'의 아형 (또는 동일한 타입)이어야 T1그것이 대한 반대 인 반면에, T2그리고 T2'. 영어로 다음과 같이 읽을 수 있습니다.

함수 A는 다른 기능의 하위 유형 B 의 파라미터를 입력하면 , A는 의 파라미터 형태의 슈퍼이고 B가 의 반환 입력 중 A는 의 리턴 타입의 서브 타입 인 B .

이 규칙의 이유는 독자에게 연습으로 남겨두고 있습니다. (힌트 : 위의 배열 예제와 같이 함수가 하위 유형화되므로 다른 경우에 대해 생각하십시오).

공분산 및 반공 분산에 대한 새로운 지식을 통해 다음 예제가 컴파일되지 않는 이유를 확인할 수 있습니다.

trait List[+A] {
  def cons(hd: A): List[A]
}

문제는 A공변량이지만 cons함수는 유형 매개 변수가 변하지 않을 것으로 예상합니다 . 따라서 A잘못된 방향으로 변화하고 있습니다. 흥미롭게도, 우리는에 List반 변형 만들어서이 문제를 해결할 수는 A있지만 List[A], cons함수가 반환 유형이 공변 이 될 것으로 기대하기 때문에 반환 유형 은 유효하지 않습니다 .

여기서 우리의 유일한 두 가지 옵션은 a) A불변성을 만들고 공분산의 훌륭하고 직관적 인 하위 입력 속성을 잃거나 b) 하한으로 cons정의 A되는 메서드에 지역 유형 매개 변수를 추가하는 것입니다 .

def cons[B >: A](v: B): List[B]

This is now valid. You can imagine that A is varying downward, but B is able to vary upward with respect to A since A is its lower-bound. With this method declaration, we can have A be covariant and everything works out.

Notice that this trick only works if we return an instance of List which is specialized on the less-specific type B. If you try to make List mutable, things break down since you end up trying to assign values of type B to a variable of type A, which is disallowed by the compiler. Whenever you have mutability, you need to have a mutator of some sort, which requires a method parameter of a certain type, which (together with the accessor) implies invariance. Covariance works with immutable data since the only possible operation is an accessor, which may be given a covariant return type.


@Daniel has explained it very well. But to explain it in short, if it was allowed:

  class Slot[+T](var some: T) {
    def get: T = some   
  }

  val slot: Slot[Dog] = new Slot[Dog](new Dog)   
  val slot2: Slot[Animal] = slot  //because of co-variance 
  slot2.some = new Animal   //legal as some is a var
  slot.get ??

slot.get will then throw an error at runtime as it was unsuccessful in converting an Animal to Dog (duh!).

In general mutability doesn't go well with co-variance and contra-variance. That is the reason why all Java collections are invariant.


See Scala by example, page 57+ for a full discussion of this.

If I'm understanding your comment correctly, you need to reread the passage starting at the bottom of page 56 (basically, what I think you are asking for isn't type-safe without run time checks, which scala doesn't do, so you're out of luck). Translating their example to use your construct:

val x = new Slot[String]("test") // Make a slot
val y: Slot[Any] = x             // Ok, 'cause String is a subtype of Any
y.set(new Rational(1, 2))        // Works, but now x.get() will blow up 

If you feel I'm not understanding your question (a distinct possibility), try adding more explanation / context to the problem description and I'll try again.

In response to your edit: Immutable slots are a whole different situation...* smile * I hope the example above helped.


You need to apply a lower bound on the parameter. I'm having a hard time remembering the syntax, but I think it would look something like this:

class Slot[+T, V <: T](var some: V) {
  //blah
}

The Scala-by-example is a bit hard to understand, a few concrete examples would have helped.

참고URL : https://stackoverflow.com/questions/663254/why-doesnt-the-example-compile-aka-how-does-co-contra-and-in-variance-w

반응형