여기에 몇 가지 문제가 있습니다. 그 중 일부는 다형성과 완전히 관련이 없습니다.
trait MyTrait {
type Repr <: MyTrait
def substitute(original: Item, replacement: Item) : Repr
def substituteAll(
originals: List[Item],
replacements: List[Item]
) : Repr = {
originals match {
case head :: tail => substitute(head).substituteAll(tail)
case Nil => this
}
}
}
substitute
및 substituteAll
모두 두 개의 인수를, 아직 한 그들을 호출을 시도하고 있습니다 :
substituteAll
방법으로 시작. 이것은 결코 작동 할 수 없다!
또한 컴파일러에 this
이 Repr
이라는 증거가 없다는 문제가 있습니다.
trait MyTrait {
type Repr <: MyTrait
def substitute(original: Item, replacement: Item) : Repr
def substituteAll(
originals: List[Item],
replacements: List[Item]
)(implicit typed: this.type => Repr) : Repr = {
def loop(pairs: List[(Item, Item)]): Repr = pairs match {
case (orig, rep) :: tail =>
substitute(orig, rep)
loop(tail)
case Nil => typed(this) //this complains that this is not of type Repr
}
loop(originals zip replacements)
}
}
내 다음 문제는 Repr
이다 : 첫 번째 문제는 다음 내부 기능을 사용 튜플 하나의리스트로 두 개의 입력을 압축하는 쉽게 충분히 고정 될 수 있고, 두 번째는 수동 증거를 제공함으로써 해결 될 수있다 type param은 복합 유형 MyTrait with MyTrait2
에 대해 보유하려는 유형을 보유하지 않습니다.
Repr
매개 변수는 여전히 추상이므로 완전한 유형은 아닙니다.당신이 원하는 정말것은이 다소 복잡 감안할 때 완전 지정된 유형 MyTrait with MyTrait2 { type Repr = MyTrait with MyTrait2 }
입니다, 그것을 표현하기 위해 다른 특성을 소개하는 것이 더 쉽습니다 :
이 trait CompoundElem extends MyTrait with MyTrait2 {
type Repr <: CompoundElem
}
당신은 다음의 나머지 부분이 사용할 수 있습니다 코드 : 당신이 조금 더의 둘레에 매달려 요소 유형을 유지하려면
case class MyClassA(originals: List[Item]) extends CompoundElem {
type Repr = MyClassA
def substitute(original: Item, replacement: Item) : MyClassA = {
val newOriginals = originals
MyClassA(newOriginals)
}
}
case class MyClassB(originals: List[Item]) extends CompoundElem {
type Repr = MyClassB
def substitute(original: Item, replacement: Item) : MyClassB = {
val newOriginals = originals
MyClassB(newOriginals)
}
}
case class CompoundClass(list : List[CompoundElem]) extends MyTrait {
type Repr = CompoundClass
def substitute(
original: Item,
replacement: Item
) = CompoundClass(
list.map(_.substitute(original, replacement))
)
}
,이 마지막 클래스도 작성할 수 있습니다 :
object MyTrait {
//type alias helper to view the type member as though it were a param
//A neat trick, shamelessly borrowed from the shapeless library
type Aux[R] = MyTrait { type Repr = R }
}
case class CompoundClass[E <: MyTrait.Aux[E]](list : List[E]) extends MyTrait {
type Repr = CompoundClass[E]
def substitute(
original: Item,
replacement: Item
) = CompoundClass(
list.map(_.substitute(original, replacement))
)
}
이렇게 작성하면 대부분의 시나리오에서 중간 특성이 필요하지 않습니다. 예를 들어, ClassA
의 List를 포함하는 CompoundClass
은 정상적으로 작동합니다. 당신이 다음 분류 된 ClassA
의 List
's 및 ClassB
의를 원하는 경우에
는 여전히 컴파일러 Repr
이해야 정확히 수습하기 위해 중간체가 필요합니다, 그것은 계산할 수 아니다이해야 또한 ClassA with ClassB
의 Repr
ClassA with ClassB
이됩니다.
당신이 완전히 요소의 유형을 삭제 괜찮다면, 당신은이 작업을 수행 할 수 있습니다
case class CompoundClass(list : List[MyTrait]) extends MyTrait {
type Repr = CompoundClass
def substitute(
original: Item,
replacement: Item
) = CompoundClass(
list.map(_.substitute(original, replacement))
)
}
그것은 단지 CompoundClass
MyTraits
의있을 거 야, 당신은 패턴 매칭 할 때를 사용해야합니다 요소를 끌어 당겨 ClassA
또는 ClassB
또는 MyTrait2
으로 처리하지만 중급 형식은 일 필요가 없습니다.
마지막으로 참조 용으로 F-Bounds를 사용하여 다시 구현 된 아이디어가 있습니다. 유형 매개 변수가 self type
수 있도록하는 방법을 참고, 그래서 당신은 명시 적으로 모든 서브 클래스에서 Repr
을 정의 할 필요가 없습니다 : 눈에서
trait Item {}
trait MyTrait {
type Repr <: MyTrait
def substitute(original: Item, replacement: Item) : Repr
def substituteAll(originals: List[Item], replacements: List[Item]) : Repr
}
object MyTrait {
trait Aux[T <: MyTrait.Aux[T]] extends MyTrait { self: T =>
type Repr = T
def substituteAll(originals: List[Item], replacements: List[Item]) : T = {
def loop(pairs: List[(Item, Item)]): Repr = pairs match {
case (orig, rep) :: tail =>
substitute(orig, rep)
loop(tail)
case Nil => this
}
loop(originals zip replacements)
}
}
}
trait MyTrait2 { }
case class MyClassA(originals: List[Item]) extends MyTrait.Aux[MyClassA] with MyTrait2 {
def substitute(original: Item, replacement: Item) = MyClassA(originals)
}
case class MyClassB(originals: List[Item]) extends MyTrait.Aux[MyClassB] with MyTrait2 {
def substitute(original: Item, replacement: Item) = MyClassB(originals)
}
case class CompoundClass(list : List[MyTrait]) extends MyTrait.Aux[CompoundClass] {
def substitute(
original: Item,
replacement: Item
) = CompoundClass(
list.map(_.substitute(original, replacement))
)
}
을, 당신이 할 수 없습니다 원하는 중 일부는 의심한다. 그러나 첫 번째 요청을 달성 할 수있는 패턴에 대해 "F-Bounded Polymophism"(스칼라에서)을 찾아보십시오. "수퍼 클래스의 메소드를 통해 변경 불가능한 클래스를 업데이트하면 동일한 유형의 구현 클래스를 반환해야합니다." –
@KevinWright 네, 저의 이전 질문을 상세히 설명했습니다. 이전 시나리오보다 더 자세하고 이전 시나리오가 작동하지 않는 시나리오에 여전히 어려움이 있습니다. – jbx
@RandallSchulz 제 첫 번째 해결책은 F-Bounded Polymorphism을 사용했습니다. MyTrait [T]로 의도 한 것입니다. 그러나 필자는 MyTrait가 인수로 기대하고 있던 곳에서 컴파일러가''T ''에 대한 몇 가지 유형을 기대하기 시작했고 어디 까지나 엉망으로 끝내는 문제에 직면했다. KevinWright (이전 질문에서)는 대신 추상적 인 형식의 멤버를 사용한다고 제안했는데, 작은 예제에서는 잘 작동하는 것처럼 보였습니다. 그러나 실제로는이 형식 멤버를 수퍼 클래스에서 반환 한 함수에'this '를 반환 할 수 없습니다 인스턴스를 매개 변수로 전달하여'# Repr'을 얻게되었습니다. – jbx