2015-01-05 5 views
7

null을 사용하고 이해를 위해 입력 주석을 처리 할 때 이상한 동작이 무엇인지 이해하려고합니다. 예를 들어형식 주석이있는 이해를위한 스칼라 예외

는 :에

def f(): String = null 

for { 
    a <- Option("hello") 
    b = f() 
} yield (a, b) 

결과 예상 :

//> res0: Option[(String, String)] = Some((hello,null)) 

그러나, 그때 내가 할 b

def f(): String = null 

for { 
    a <- Option("hello") 
    b: String = f() 
} yield (a, b) 

의 종류에 유형 약어를 추가하는 경우 런타임 예외 :

//> scala.MatchError: (hello,null) (of class scala.Tuple2) 

왜 이런 일이 발생합니까? 어쨌든 b은 암시 적으로 첫 번째 예제에서 String 유형입니까? 두 번째 예제의 명시 적 유형 주석은 어떻게 변경됩니까?

(참고, 예 스칼라 2.11.4에서 실행 된)

+0

당신이 REPL에서'reify'를 사용하여 볼 수있는 두 번째 예는, 일치로 컴파일 :'수입 scala.reflect.runtime.universe ._; reify {for {...}}'. 왜 당신에게 말할 수는 없지만. AIUI'null'은 일치하지 않습니다. 왜냐하면'b'는'String'의 컴파일 타임 타입을 가지고 있습니다. 이것은 어떤 의미에서는 타입 시스템의 구멍이며, scala 코드는 일반적으로 null을 사용하지 않아야합니다. – lmm

+0

나는 nulls의 팬이 아니다, 나를 믿어 라! 실제 코드에서 나는 이것을 우연히 발견했는데'Try's를 사용하여 일부 레거시 Java 코드와 통합했습니다. 그러나 null은 나에게 부수적 인 것처럼 보입니다 : 기괴한 (무서운 것)이라는 측면은 (보다 직관적이지 않은) 런타임 예외에 더 많은 유형 지정 결과를 추가하는 것입니다. –

+0

'for// yield'형식을 사용하면 패턴 일치가 발생한다는 것은 이상한 언어입니다. 표현식에서 "일반"유형으로 발생하는 문제는 아닙니다. 내가 말할 수있는 것은 불행한 일이지만,이 단계에서는 아마도 바뀔 수 없다. 나는 이런 이유로 왼쪽/오른쪽에 타입을주는 것을 피하는 경향이있다./ – lmm

답변

7

null 아무것도의 인스턴스가 아닌 :

scala> (null: String) match { case _: String => } 
scala.MatchError: null 
    ... 33 elided 

scala> val s: String = null 
s: String = null 

scala> s.isInstanceOf[String] 
res1: Boolean = false 

http://www.scala-lang.org/files/archive/spec/2.11/08-pattern-matching.html#type-patterns

형 패턴은 null이 아닌 지정합니다.

번역을 보여주는 하나의 트릭 쇼를 언급하는 것입니다

:

scala> for { 
    | a <- Option("hello") 
    | b: String = f() 
    | } yield (a, b) // show 
object $read extends scala.AnyRef { 
    def <init>() = { 
    super.<init>; 
    () 
    }; 
    object $iw extends scala.AnyRef { 
    def <init>() = { 
     super.<init>; 
    () 
    }; 
    import $line4.$read.$iw.$iw.f; 
    object $iw extends scala.AnyRef { 
     def <init>() = { 
     super.<init>; 
     () 
     }; 
     val res1 = Option("hello").map(((a) => { 
     val b: String = f; 
     scala.Tuple2(a, b) 
     })).map(((x$1) => x$1: @scala.unchecked match { 
     case scala.Tuple2((a @ _), (b @ (_: String))) => scala.Tuple2(a, b) 
     })) 
    } 
    } 
} 
scala.MatchError: (hello,null) (of class scala.Tuple2) 
    at $anonfun$2.apply(<console>:10) 
    at $anonfun$2.apply(<console>:10) 
    at scala.Option.map(Option.scala:145) 
    ... 39 elided 
+0

그게 잘못되어 가고있는 것을 보여줍니다. 그러나 그것은 틀림없이 버그입니다. 그 이유는 construction_가 이미 올바른 타입 (val b : String = f')을 가지고 있기 때문에 그 마지막 맵에서 패턴 매치 할 이유가 없기 때문입니다. (또한 신원 확인 기능이 최적화되지 않은 것은 논란의 여지가 있지만 더 복잡한 경우를 상상할 수 있습니다.) –

+0

@RexKerr 예, 틀림없이. 오래된'val b : String = f()'문법을 사용했다면 강력한 arg입니다. 아마도 생성자에 pat이라는 타입이 필터를 의미하는 modulo SI-900 일 것입니다. 하지만 평범한 경기를 최적화하지는 않을 것입니다. 어쩌면 우리는 'val i = 42 : Int'라고 써야 할 것입니다. 그렇다면'val s : String = null : String'은'Tuple1'에서 래핑 된 것처럼 실패 할 수 있습니다. –

+0

언어 스펙은 이해에서 형식 주석의 탈 설탕 제거가 패턴 일치로 구현되도록 지시합니까? –