2014-02-10 4 views
0

나는이 문제를 해결하기 위해 노력했지만이 문제에 대한 해결책을 찾을 수없는 것 같습니다. 스칼라에서 제대로 모델링 할 수없는 것 같습니다.변경 불가능한 클래스의 다형성 업데이트

내가 구현하는 불변 클래스가있는 MyTrait이라는 특성이 있다고 가정 해 보겠습니다.

그리고 다음과 같은 :

  • 을 슈퍼 특성의 방법을 통해 불변 클래스 업데이트 : 나는 다음과 같이 그들은 것 내 문제를 요약한다면

    trait MyTrait { 
        type Repr <: MyTrait 
    
        def substitute(original: Item, replacement: Item) : Repr 
    
        def substituteAll(
        originals: List[Item], 
        replacement: Item 
    ) : Repr = { 
        originals match { 
         case head :: tail => substitute(head).substituteAll(tail, replacement) 
         case Nil => this //this complains that this is not of type Repr 
        }  
        } 
    } 
    
    trait MyTrait2 { ... } 
    
    case class MyClassA(originals: List[Item]) 
    extends MyTrait with MyTrait2 { 
        type Repr = MyClassA 
    
        def substitute(original: Item, replacement: Item) : MyClassA = { 
        //whatever code that updates the list etc. 
    
        MyClassA(newOriginals) 
        }  
    } 
    
    case class MyClassB(originals: List[Item]) 
    extends MyTrait with MyTrait2 { 
        type Repr = MyClassB 
    
        def substitute(original: Item, replacement: Item) : MyClassB = { 
        //whatever code that updates the list etc. 
    
        MyClassB(newOriginals) 
        }  
    } 
    
    case class CompoundClass(list : List[MyTrait with MyTrait2]) 
    extends MyTrait { 
        type Repr = CompoundClass 
    
        def substitute(
        original: Item, 
        replacement: Item 
    ) : CompoundClass = 
        CompoundClass(list.map(
         myClass => myClass.substitute(original, replacement) 
        )) 
    ) 
        //the above complains that it is expecting 
        // List[MyTrait with MyTrait2] 
        //while in fact it is getting MyTrait2#Repr 
    } 
    

    같은 종류의 구현 클래스를 반환해야합니다.

  • 수퍼 형질은 다른 객체를 반환하기보다는 this을 반환 할 수 있어야합니다. 나는 이것에 문제가있는 것 같다.

  • 실제 유형을 모른 채 슈퍼 - 특성을 기능에 전달할 수 있어야합니다. 표준 다형성. 따라서 함수는 실제 구체적인 유형을 아는 것없이 특성의 메소드를 호출 할 수 있습니다.

  • 수업을 결합하여 다른 수업에 조합 할 수 있어야합니다. (하위 표현식으로 구성된 표현식을 상상해보십시오). 그것은 표준 구성처럼 보이지만, 위와 나는 원래 MyTrait[T]에서 제네릭 형식을 사용하여 시도했다 #Repr

으로 이러한 오류를 얻고있다 그러나 이것은 불가능 내가 원하는 구체적인 클래스를 통과 할 수 있습니다. 나는 추상적 인 타입을 사용하려고 노력하고 있는데, 나는 컴파일 타임에 같은 문제에 직면 해있는 것처럼 보인다. 근본적으로 나는 지금 정말로 차이가 없다고 생각한다. 나는 다시 같은 함정에 빠지게된다.

내가 뭘 잘못하고 있니? 나는 이것을 잘못된 방향으로보고 있는가?

+0

을, 당신이 할 수 없습니다 원하는 중 일부는 의심한다. 그러나 첫 번째 요청을 달성 할 수있는 패턴에 대해 "F-Bounded Polymophism"(스칼라에서)을 찾아보십시오. "수퍼 클래스의 메소드를 통해 변경 불가능한 클래스를 업데이트하면 동일한 유형의 구현 클래스를 반환해야합니다." –

+0

@KevinWright 네, 저의 이전 질문을 상세히 설명했습니다. 이전 시나리오보다 더 자세하고 이전 시나리오가 작동하지 않는 시나리오에 여전히 어려움이 있습니다. – jbx

+0

@RandallSchulz 제 첫 번째 해결책은 F-Bounded Polymorphism을 사용했습니다. MyTrait [T]로 의도 한 것입니다. 그러나 필자는 MyTrait가 인수로 기대하고 있던 곳에서 컴파일러가''T ''에 대한 몇 가지 유형을 기대하기 시작했고 어디 까지나 엉망으로 끝내는 문제에 직면했다. KevinWright (이전 질문에서)는 대신 추상적 인 형식의 멤버를 사용한다고 제안했는데, 작은 예제에서는 잘 작동하는 것처럼 보였습니다. 그러나 실제로는이 형식 멤버를 수퍼 클래스에서 반환 한 함수에'this '를 반환 할 수 없습니다 인스턴스를 매개 변수로 전달하여'# Repr'을 얻게되었습니다. – jbx

답변

1

여기에 몇 가지 문제가 있습니다. 그 중 일부는 다형성과 완전히 관련이 없습니다.

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 
    }  
    } 
} 

substitutesubstituteAll 모두 두 개의 인수를, 아직 한 그들을 호출을 시도하고 있습니다 :

substituteAll 방법으로 시작. 이것은 결코 작동 할 수 없다!

또한 컴파일러에 thisRepr이라는 증거가 없다는 문제가 있습니다.

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은 정상적으로 작동합니다. 당신이 다음 분류 된 ClassAList 's 및 ClassB의를 원하는 경우에

는 여전히 컴파일러 Repr이해야 정확히 수습하기 위해 중간체가 필요합니다, 그것은 계산할 수 아니다이해야 또한 ClassA with ClassBReprClassA 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)) 
) 
} 

그것은 단지 CompoundClassMyTraits의있을 거 야, 당신은 패턴 매칭 할 때를 사용해야합니다 요소를 끌어 당겨 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)) 
) 
} 
+0

감사합니다. 'substituteAll' 문제는 여기에 표시 할 간단한 예제로 코드를 추상화 할 때 단지 오타 였고 실수를 너무 피곤했습니다. 너는 내가 어쨌든 의미했던 것을 가지고있다. 자세한 내용은 귀하의 답변을보고 ... – jbx

+0

그래서, (암시 적으로 입력 : this.type => Repr)'훌륭하게 작동합니다. 내가 그곳에 타입을 지정할 수 있는지 몰랐습니다. 나는 아직도 'Repr'에 관해서는 아직 꽤있다. 'MyTrait'는 구체적인 계급이 실제로 다른 특성을 구현하는지 왜주의해야합니까? 나는 아마도 내가 다른 구체적인 수업 (나는 약 10 개를 가지고 있으며 그것들은 기본 하나와 관련없는 4 개 또는 5 개의 다른 형질을 혼합하고 일치시킬 수있는 모든 가능한 형질을 수용 할 수 없다. 왜 나는 'Repr'이 'MyClassA'라고 단순히 말하면 안되며 MyTrait를 확장하는 한 그것이 다른 특성을 구현하는지 상관하지 않는 이유는 무엇입니까? – jbx

+0

마지막 예제 + 단락을 조정하면 도움이 될 것입니다. –

관련 문제