2012-07-01 3 views
9

여기에는 두 특성, 이전 특성으로 경계가 지정된 공변 유형 매개 변수가있는 클래스 및 다른 클래스로 묶인 형식 매개 변수가있는 두 번째 특성을 사용하는 간단한 설정이 나와 있습니다. 두 클래스 모두에 대해 두 가지 특성 중 하나가 type 매개 변수 밑에있는 경우에만 특정 메서드를 사용할 수 있습니다 (암시 적 증거를 통해). 이 잘 컴파일 : 그러나스칼라 : 형식 매개 변수를 사용하는 클래스의 암시 적 증거

trait Foo 
trait ReadableFoo extends Foo {def field: Int} 

case class Bar[+F <: Foo](foo: F) { 
    def readField(implicit evidence: F <:< ReadableFoo) = foo.field 
} 

case class Grill[+F <: Foo, +B <: Bar[F]](bar: B) { 
    def readField(implicit evidence: F <:< ReadableFoo) = bar.readField 
} 

, BarF에, 나는 Grill에서 F 매개 변수를 필요가 없습니다 공변 때문이다. 그저 BBar[ReadableFoo]의 하위 유형 일 것을 요구해야합니다. 이 그러나 실패 : 오류가있는

case class Grill[+B <: Bar[_]](bar: B) { 
    def readField(implicit evidence: B <:< Bar[ReadableFoo]) = bar.readField 
} 

:

error: Cannot prove that Any <:< this.ReadableFoo. 
    def readField(implicit evidence: B <:< Bar[ReadableFoo]) = bar.readField 

가 왜 암시 적 증거가 고려되고 있지?

답변

6

증거 인스턴스 <:<B에서 Bar[ReadableFoo]으로의 암시 적 변환을 허용하므로 bar.readField 호출이 가능합니다.

readField을 호출하는 문제는 연속적인 증거 매개 변수 F <:< ReadableFoo이 필요하다고 생각합니다. 내 생각에 컴파일러는 Bar의 형식 매개 변수를 암시 적 해결책의 첫 번째 검색 단계에서 완전히 대체하지 않습니다 (readField을 찾으므로 처음에는 Bar이 필요합니다). 그리고 내가 아는 한 '되돌아 오는 추적'의 형태가 없기 때문에 두 번째 암시 적 해결책에 대해 질식합니다.

어쨌든. 수 있습니다 또 다른 가능성이있다

case class Grill[+B <: Bar[_]](bar: B) { 
    def readField(implicit evidence: B <:< Bar[ReadableFoo]) = evidence(bar).readField 
} 

case class Grill[+B <: Bar[_]](bar: B) { 
    def readField(implicit evidence: B <:< Bar[ReadableFoo]) = 
    implicitly[Bar[ReadableFoo]](bar).readField 
} 

: 좋은 점은 <:<apply 방법을 사용하거나 도우미 방법 implicitly를 사용하여 컴파일러보다 더 알고 명시 적으로 변환 참여를 유도 할 수 중 하나이며, 이 @Kaito 알 수 있듯이 문제가 될 수 <:<의 구현에 의존하지 않는, 깨끗한 :

case class Grill[+B <: Bar[_]](bar: B) { 
    def readField(implicit evidence: B <:< Bar[ReadableFoo]) = 
    (bar: Bar[ReadableFoo]).readField 
} 
+0

나는 <잘 모르겠어요 : <호출하기위한 것입니다, 첫 번째 매개 변수가 두 번째 매개 변수의 하위 유형 인 경우 존재하는 값. 구현은 기본적으로 신원 기능입니다. 이 예제는 기본적으로 컴파일러가 readField 메소드의 암시 적 매개 변수를 찾는 데 도움이되는 (안전한) 유형 변환을 수행하기 때문에 작동합니다. – Kaito

+0

@Kaito :''<''''apply''는 호출 할 의도가 없다는 증거가 있습니까? 그것은 [문서화 된 동작]입니다 (http://www.scala-lang.org/api/rc/scala/Predef$$$less$colon$less.html). '(bar : Bar [ReadableFoo]). readField'라고 쓰면 암시 적 변환이 자동으로 시작되지만 Sciss의 버전은 나에게 더 깨끗하게 느껴진다. –

+0

@TravisBrown : 없음 그러나 실제로 Functional1 특성에 대한 설명을 상속받은 사실을 구두로 문서화하는 라인을 찾을 수는 없습니다. 그 행동에 의지 할 수 있는지 잘 모르겠지만, 다음 버전에서는 예외를 던질 수도 있습니다. – Kaito

5

0 __의 대답 (사용 권한 유형으로 bar를 설정하는 암시 적 증거 인수) 답변입니다 나는 특정 퀘스트를 줄 것이다. 당신이 묻는 이온 (비록 내가 암시적인 주장을 가지고 있다면 implicitly을 사용하지 말 것을 권하고 싶지만).

당신이 묘사하고있는 상황이 유형 클래스를 통한 ad-hoc 다형성의 좋은 유스 케이스일지도 모르겠다.새로운 인스턴스를 생성 및 범위에 인스턴스가 모든 유형 위에 readField 방법을 포주에 대한 몇 가지 편리한 방법과 함께,

trait Foo 
case class Bar[F <: Foo](foo: F) 
case class Grill[B <: Bar[_]](bar: B) 

그리고 형 클래스 : 우리는 다음과 같은 설정을 가지고 있음을 예를 들어 말해

trait Readable[A] { def field(a: A): Int } 

object Readable { 
    def apply[A, B: Readable](f: A => B) = new Readable[A] { 
    def field(a: A) = implicitly[Readable[B]].field(f(a)) 
    } 

    implicit def enrich[A: Readable](a: A) = new { 
    def readField = implicitly[Readable[A]].field(a) 
    } 
} 

import Readable.enrich 

그리고 인스턴스의 몇 : 마지막으로

implicit def barInstance[F <: Foo: Readable] = Readable((_: Bar[F]).foo) 
implicit def grillInstance[B <: Bar[_]: Readable] = Readable((_: Grill[B]).bar) 

그리고 읽을 수 Foo :

case class MyFoo(x: Int) extends Foo 

implicit object MyFooInstance extends Readable[MyFoo] { 
    def field(foo: MyFoo) = foo.x 
} 

이것은 우리가 예를 들어, 다음을 수행 할 수 있습니다

scala> val readableGrill = Grill(Bar(MyFoo(11))) 
readableGrill: Grill[Bar[MyFoo]] = Grill(Bar(MyFoo(11))) 

scala> val anyOldGrill = Grill(Bar(new Foo {})) 
anyOldGrill: Grill[Bar[java.lang.Object with Foo]] = Grill(Bar([email protected])) 

scala> readableGrill.readField 
res0: Int = 11 

scala> anyOldGrill.readField 
<console>:22: error: could not find implicit value for evidence parameter of 
type Readable[Grill[Bar[java.lang.Object with Foo]]] 
       anyOldGrill.readField 
      ^

을 우리가 원하는 어느.

1

이 질문에 대한 답이 아니라, '형 제약은'정말 암시 적 변환입니다 것을 보여주기 위해 :

Welcome to Scala version 2.9.1.final (Java HotSpot(TM) 64-Bit Server VM, Java 1.6.0_33). 
Type in expressions to have them evaluated. 
Type :help for more information. 

scala> trait A { def test() {} } 
defined trait A 

scala> class WhatHappens[T] { def test(t: T)(implicit ev: T <:< A) = t.test() } 
defined class WhatHappens 

scala> :javap -v WhatHappens 
... 
public void test(java.lang.Object, scala.Predef$$less$colon$less); 
    Code: 
    Stack=2, Locals=3, Args_size=3 
    0: aload_2 
    1: aload_1 
    2: invokeinterface #12, 2; //InterfaceMethod scala/Function1.apply:(Ljava/lang/Object;)Ljava/lang/Object; 
    7: checkcast #14; //class A 
    10: invokeinterface #17, 1; //InterfaceMethod A.test:()V 
    15: return 
... 
    LocalVariableTable: 
    Start Length Slot Name Signature 
    0  16  0 this  LWhatHappens; 
    0  16  1 t  Ljava/lang/Object; 
    0  16  2 ev  Lscala/Predef$$less$colon$less; 
... 
+0

나는 그것이 어떻게 작동하는지 알고있다. 내가 묻는 질문은 의도 된 행동인지 여부와 미래의 업데이트에 의존 할 수 있는지 여부입니다. 설명서에는 유형 제약 조건이 포함되어 있지만 암시 적 변환으로는 사용되지 않는다고 언급되어 있습니다. – Kaito

관련 문제