[SCALA] 어떻게 기능의 요소의 공변 및 contravariant 위치를 확인하려면?
SCALA어떻게 기능의 요소의 공변 및 contravariant 위치를 확인하려면?
이것은 내가 스칼라 contravariance 및 공분산에 대한 읽을 수있는 기사 중 하나에서 코드 조각입니다. 공변 A 형은 값 pet2의 A 형에 contravariant 위치에서 발생 그러나, 나는 스칼라 컴파일러 "오류가 발생합니다 오류 메시지를 이해하지 못하는
class Pets[+A](val pet:A) {
def add(pet2: A): String = "done"
}
이 코드의 나의 이해는 공변하고 그러나 A의 아형 개체를 받아들이는 애완 동물, 기능 추가가 애완 동물 유형 A와 부속 유형의 매개 변수를 취할 수있는 A 형 only.Being 공변 수단의 매개 변수에 걸리는 것입니다. 그런 다음이 오류가 발생하는 방법을 생각한다. 어디에서 심지어 발생 contravariance의 문제이다.
위의 오류 메시지에 대한 모든 설명은 매우 도움이 될 것입니다. 감사
해결법
-
==============================
1.TL; DR :
TL; DR :
유일하게 남아있는 가능성이 : 애완 동물 A.에서 일정해야합니다
나는 개선 및 훨씬 더 제시하는이 기회를 사용합니다 이 만화의 엄격한 버전. 그것은 공분산 및 contravariance의 그림입니다 하위 유형 및 신고 사이트 분산 주석과 프로그래밍 언어에 대한 개념 (분명히, 심지어 자바 사람들은 충분히 계몽 발견 문제는) 사용 현장의 변화에 대한 있다는 사실에도 불구하고.
첫째, 그림 :
이제 컴파일 가능한 스칼라 코드에 대한 자세한 설명.
매우 구체적으로, 매우 일반적인에서, 에너지 원의 다음과 같은 계층 구조를 고려 :
class EnergySource class Vegetables extends EnergySource class Bamboo extends Vegetables
이제 특성 소비자를 고려 [-A] 하나의 소비가 그 (A : A) -method :
trait Consumer[-A] { def consume(a: A): Unit }
이제이 특성의 몇 가지 예를 구현 보자 :
object Fire extends Consumer[EnergySource] { def consume(a: EnergySource): Unit = a match { case b: Bamboo => println("That's bamboo! Burn, bamboo!") case v: Vegetables => println("Water evaporates, vegetable burns.") case c: EnergySource => println("A generic energy source. It burns.") } } object GeneralistHerbivore extends Consumer[Vegetables] { def consume(a: Vegetables): Unit = a match { case b: Bamboo => println("Fresh bamboo shoots, delicious!") case v: Vegetables => println("Some vegetables, nice.") } } object Panda extends Consumer[Bamboo] { def consume(b: Bamboo): Unit = println("Bamboo! I eat nothing else!") }
이제, 왜 소비자가에 contravariant해야합니까? 인스턴스화하는하자의 시도 몇 가지 다른 에너지 원하고 다양한 소비자에게 공급 :
val oilBarrel = new EnergySource val mixedVegetables = new Vegetables val bamboo = new Bamboo Fire.consume(bamboo) // ok Fire.consume(mixedVegetables) // ok Fire.consume(oilBarrel) // ok GeneralistHerbivore.consume(bamboo) // ok GeneralistHerbivore.consume(mixedVegetables) // ok // GeneralistHerbivore.consume(oilBarrel) // No! Won't compile Panda.consume(bamboo) // ok // Panda.consume(mixedVegetables) // No! Might contain sth Panda is allergic to // Panda.consume(oilBarrel) // No! Pandas obviously cannot eat crude oil
결과는 다음과 같습니다 GeneralistHerbivore이 소비 할 수있는 화재는 모든 것을 소비 할 수있는, 차례로 GeneralistHerbivore는 팬더가 먹을 수있는 모든 것을 소비 할 수 있습니다. 따라서만큼 우리는 에너지를 소비 할 수있는 능력에 대해 신경으로, 소비자가 [야채]가 요구되는 경우 소비자는 [EnergySource 같이, 치환 될 수있는 과 소비자 [대나무]가 요구되는 경우 소비자는 [야채]는 치환 될 수있다. 따라서, 의미가 그 소비자 [EnergySource] <: 소비자 [야채] 및 소비자 [야채] <: 소비자 [대나무]에도 관계하지만 사이 유형 매개 변수는 정반대입니다 :
type >:>[B, A] = A <:< B implicitly: EnergySource >:> Vegetables implicitly: EnergySource >:> Bamboo implicitly: Vegetables >:> Bamboo implicitly: Consumer[EnergySource] <:< Consumer[Vegetables] implicitly: Consumer[EnergySource] <:< Consumer[Bamboo] implicitly: Consumer[Vegetables] <:< Consumer[Bamboo]
제품의 계층 구조를 정의합니다 :
class Entertainment class Music extends Entertainment class Metal extends Music // yes, it does, seriously^^
A 형의 값을 생성 할 수있는 특성을 정의합니다 :
trait Producer[+A] { def get: A }
다양한 "소스"전문 다양한 수준의 / "생산자"를 정의합니다 :
object BrowseYoutube extends Producer[Entertainment] { def get: Entertainment = List( new Entertainment { override def toString = "Lolcats" }, new Entertainment { override def toString = "Juggling Clowns" }, new Music { override def toString = "Rick Astley" } )((System.currentTimeMillis % 3).toInt) } object RandomMusician extends Producer[Music] { def get: Music = List( new Music { override def toString = "...plays Mozart's Piano Sonata no. 11" }, new Music { override def toString = "...plays BBF3 piano cover" } )((System.currentTimeMillis % 2).toInt) } object MetalBandMember extends Producer[Metal] { def get = new Metal { override def toString = "I" } }
BrowseYoutube 엔터테인먼트의 가장 일반적인 소스입니다 : 그것은 당신에게 줄 수 엔터테인먼트 기본적으로 어떤 종류 : 고양이 비디오, 저글링 광대, 또는 (실수로) 어떤 음악. 엔터테인먼트의이 일반적인 소스는 그림 1의 원형 적 광대로 표시됩니다.
RandomMusician 이미 다소 적어도 우리는이 객체가 알고, 전문 (특정 장르에 제한이 없다하더라도) 음악을 생산하고 있습니다.
마지막으로, MetalBandMember는 매우 전문 : get 메소드가 리턴에 보장 금속 음악 만 매우 구체적인 종류.
이제이 세 가지 목적에서 엔터테인먼트의 다양한 종류를 얻으려고하자 :
val entertainment1: Entertainment = BrowseYoutube.get // ok val entertainment2: Entertainment = RandomMusician.get // ok val entertainment3: Entertainment = MetalBandMember.get // ok // val music1: Music = BrowseYoutube.get // No: could be cat videos! val music2: Music = RandomMusician.get // ok val music3: Music = MetalBandMember.get // ok // val metal1: Entertainment = BrowseYoutube.get // No, probably not even music // val metal2: Entertainment = RandomMusician.get // No, could be Mozart, could be Rick Astley val metal3: Entertainment = MetalBandMember.get // ok, because we get it from the specialist
우리는 세 가지 프로듀서 [엔터테인먼트], 프로듀서 [음악]와 프로듀서 [금속] 엔터테인먼트의 어떤 종류를 생산할 수있는 것을 알 수있다. 우리는 단지 프로듀서 [음악]와 프로듀서 [금속] 농산물 음악에 보장을 참조하십시오. 마지막으로, 우리는 매우 전문 프로듀서가 [금속]이 보장되는 것을 볼 수 금속 및 아무것도를 생산하고 있습니다. 따라서, 생산자 [음악] 및 프로듀서 [금속]는 치환 될 수있는 프로듀서 대 [엔터테인먼트]. 프로듀서 [금속]가 프로듀서 [음악]로 치환 될 수있다. 일반, 프로듀서에서 제품에 대한보다 구체적인 종류는 덜 전문 프로듀서 녹실 할 수 있습니다 :
implicitly: Metal <:< Music implicitly: Metal <:< Entertainment implicitly: Music <:< Entertainment implicitly: Producer[Metal] <:< Producer[Music] implicitly: Producer[Metal] <:< Producer[Entertainment] implicitly: Producer[Music] <:< Producer[Entertainment]
제품 하위 유형 간의 관계는 하위 유형 간의 관계와 동일 제품의 생산. 이것은 무엇 공분산 방법이다.
관련된 링크들
-
==============================
2.(가 + A로 표시 있기 때문에) 클래스 애완 동물은 A 형에 공변입니다,하지만 당신은 contravariant 위치에 그것을 사용하고 있습니다. 이 스칼라 함수의 특성을 살펴 경우, 당신은 반환 형식은 공변 상태에서 입력 매개 변수 유형, contravariant 것을 볼 수 있기 때문이다. 모든 함수는 반환 형식에 입력 유형에 contravariant과 공변입니다.
(가 + A로 표시 있기 때문에) 클래스 애완 동물은 A 형에 공변입니다,하지만 당신은 contravariant 위치에 그것을 사용하고 있습니다. 이 스칼라 함수의 특성을 살펴 경우, 당신은 반환 형식은 공변 상태에서 입력 매개 변수 유형, contravariant 것을 볼 수 있기 때문이다. 모든 함수는 반환 형식에 입력 유형에 contravariant과 공변입니다.
예를 들어, 하나 개의 인수를 복용 함수는 다음과 같이 정의되어있다
trait Function1[-T1, +R]
함수는 함수 F의 하위 유형으로 S에 대한 건은, 그것은 "이하 (같거나)이 필요하고 더 (동일하거나)를 제공"할 필요가있다. 이 또한 Liskov 대체 원칙으로 알려져있다. 실제로,이 방법은 함수의 특성 요구는 출력 및 입력에 공변 contravariant 될 것이다. (우리는 제한, 예컨대 과일에서 식품을 완화하기 때문에 여기에서 "낮은"수단 "수퍼")를 그 어느 T1 또는 슈퍼 타입 중 하나를 수용하기 때문에 입력에 contravariant 존재함으로써 "동일 이하"이 필요하다. 또한, 자사의 반환 형식에서 공변 인으로, 우리가 과일에서 예를 들어, 더 많은 정보를 추가하고 있기 때문에 "더"수단 "하위"여기 (보다 R을 반환하거나 아무것도 더 구체적인 수 있다는 것을 의미한다 "동일하거나 더"요구 사과).
그런데 왜? 왜 다른 방법의 주위에? 여기에 희망을보다 직관적으로 설명 할 것입니다 예입니다 - 두 개의 콘크리트 기능, 다른 하나 개 존재 하위 유형을 상상은 :
val f: Fruit => Fruit val s: Food => Apple
덜 (과일에서 식품에가는 우리 "잃게"정보)를 필요로하고 더 (과일에서 애플에가는 우리 "이득"정보)를 제공하기 때문에 기능들, 함수 f에 대한 유효한 하위 유형이다. s는 F의 입력 형식 (contravariance)의 슈퍼의 입력 형태를 가지고, 그것은 F의 복귀 형 (공분산)의 부속의 타입을 반환하는 방법을 참고. 이제 이러한 기능을 사용하는 코드의 조각을 가정 해 봅시다 :
def someMethod(fun: Fruit => Fruit) = // some implementation
둘 것으로 someMethod (F)와 것으로 someMethod (들) 유효 호출합니다. 방법 것으로 someMethod 그것에 열매를 적용하고, 그것에서 열매를받을 내부적으로 재미를 사용합니다. 의 우리가 식품을 공급할 수있는 수단은 F의 서브 타입이기 때문에 => 애플의 재미를 완벽하게 좋은 예를 역할을합니다. 것으로 someMethod 내부 코드 것이다 재미 음식을 취하고, 과일 음식이기 때문에, OK 일부 과일, 몇 가지 점 피드 재미에서. 재미 과일을 반환해야하기 때문에 다른 한편으로는, 반환 형식으로 애플을 재미, 좋은 또한, 사과를 반환하여 그 계약을 준수합니다.
나는 그것을 조금 명확하게 관리하겠습니다 더 질문을 주시기 바랍니다.
from https://stackoverflow.com/questions/48812254/how-to-check-covariant-and-contravariant-position-of-an-element-in-the-function by cc-by-sa and MIT license
'SCALA' 카테고리의 다른 글
[SCALA] 스파크 : DB 점화 RDD 파티션 당 연결 및 수행 mapPartition (0) | 2019.11.22 |
---|---|
[SCALA] 스칼라에서 병렬 파일 처리 (0) | 2019.11.22 |
[SCALA] 스칼라 SBT와 기업의 프록시 - SunCertPathBuilderException (0) | 2019.11.22 |
[SCALA] 임베디드 스칼라 REPL 상속 부모 클래스 경로 (0) | 2019.11.22 |
[SCALA] 사람이 기호가 "=>"스칼라에서 어떻게 사용되는지 설명 할 수 (0) | 2019.11.22 |