2016-10-10 6 views
3

나는 스칼라에 servant-server 포트에서 작업 중입니다. 아이디어는 typecass 해상도를 사용하여 요청을 처리 할 수있는 함수를 유도 적으로 구축하는 것입니다. 나는 꽤 이상하게 추측 할 수없는 이상한 추론을하고있다.스칼라는 유도 적으로 빌드 된 경로 종속 유형을 추측 할 수 없습니다.

val foo = implicitly[HasServer[Int :> Unit]] 
implicitly[=:=[Int => String, foo.ServerT]]           

오류는 다음과 같습니다 : 그러나

servant.scala:33: error: 
Cannot prove that Int => String =:= Main.$anon.Servant.foo.ServerT. 

, 내가 통해 직접 HasServer[Int :> Unit]를 인스턴스화하는 경우가 컴파일 위의와

object Servant {                  

    class :>[Path, A]                 

    trait HasServer[A] {                
    type ServerT                  

    def route(a: ServerT): String              
    }                     

    implicit val unitServer = new HasServer[Unit] {          
    type ServerT = String                

    def route(str: ServerT): String = str            
    }                     

    implicit def subServer[A, Sub](implicit sub: HasServer[Sub]) = new HasServer[A :> Sub] { 
    type ServerT = A => sub.ServerT             

    def route(handler: ServerT): String = "handler"         
    } 

} 

는, 다음은 컴파일에 실패 :

val foo = new HasServer[Int :> Unit] {            
    type ServerT = Int => unitServer.ServerT           

    def route(handler: ServerT): String = handler(10)         
    } 

어떻게 이것을 컴파일 할 수 있습니까? 감사!

답변

8

문제는

def implicitly[T](implicit e: T) = e 

implicitly[T]은 오직 당신에게 T로 입력되는 값보다 정확 결코 아무것도 줄 것이다 ... 모든 implicitly의 정의입니다. 위의 경우는 HasServer[Int :> Unit]이며 결정적으로 구성원 유형은 ServerT입니다. 이 결합하는 것이 일반적인 그래서

일반적으로 입력 클래스 동반자 개체 당 원하는 정제를, 예. 보존 apply 방법, 결과 여기에 입력
object HasServer { 
    def apply[T](implicit hs: HasServer[T]): 
    HasServer[T] { type ServerT = hs.ServerT } = hs 
} 

조금 다루기 인을 정의하여 해결할한다 이 "Aux"패턴,

object HasServer { 
    type Aux[T, S] = HasServer[T] { type ServerT = S } 
    def apply[T](implicit hs: HasServer[T]): Aux[T, hs.ServerT] = hs 
} 

어떤 경우에도 다른 곳에 유용 할 것입니다.

우리는 이것이 REPL에서 추론 유형에 만드는 차이를 볼 수

scala> implicitly[HasServer[Int :> Unit]] 
res0: Servant.HasServer[Servant.:>[Int,Unit]] = ... 

scala> HasServer[Int :> Unit] 
res1: Servant.HasServer[Servant.:>[Int,Unit]]{type ServerT = Int => String} = ... 

이 정제가하는 발 정의의 유형으로 추정됩니다

그래서 지금 당신이 원하는 결과를 얻을 수 있습니다,

scala> val foo = HasServer[Int :> Unit] 
foo: Servant.HasServer[Servant.:>[Int,Unit]]{type ServerT = Int => String} = ... 

scala> implicitly[=:=[Int => String, foo.ServerT]] 
res2: =:=[Int => String,foo.ServerT] = <function1> 

이 문제를 방지하기 위해 implicitly의 정의를 개선 할 수있는 여러 가지 방법이 있습니다. 다음은,

def implicitly[T <: AnyRef](implicit t: T): t.type = t 

참조 유형에 대한 가장 간단하고 literal types을 사용하는 경우 우리는

def implicitly[T](implicit t: T): t.type = t 

볼품이 유사하게 작동 the[T] 연산자를 제공는 <: AnyRef 바인딩 및 모든 유형을 정의 제거 할 수 매크로를 통해 후자에게.

+0

저는 재미 있어요, 저는 2 년 동안 Aux 트릭을 사용했고, 결코 암묵적으로 의존하지 않았고, 암묵적으로 당신이 묻는 것보다 더 정확한 타입을 반환하지 않는다는 것을 알게되었습니다.) – mandubian