유형 매개 변수가 지정되지 않은 경우 Scala가 Bottom Type을 추론하는 이유는 무엇입니까?
아래의이 특정 사례에서 추론 규칙을 설명 할 수있는 사람이 있는지 궁금합니다. 가장 중요한 것은 합리적 / 함축입니까?
case class E[A, B](a: A) // class E
E(2) // E[Int,Nothing] = E(2)
내가 썼을 수 있음을 유의하십시오 E[Int](2)
. 나에게 중요한 것은 두 번째 매개 변수 유형이 Nothing
예 Any
를 들어 말하지 않고 (즉, Bottom 유형)으로 추론되는 이유입니다 . 그 이유는 무엇이며 합리적 / 함의는 무엇입니까?
약간의 맥락을 제공하기 위해 이것은 Either의 정의와 왼쪽 및 오른쪽에서 작동하는 방식과 관련이 있습니다. 둘 다 패턴에 따라 정의됩니다.
final case class X[+A, +B](value: A) extends Either[A, B]
인스턴스화하는 곳 Right[Int](2)
은 다음 Right[Nothing, Int]
과 같습니다.Either[Nothing, Int]
편집 1
여기에는 일관성이 있지만 여전히 합리성을 파악할 수 있습니다. 아래는 반 변형 매개 변수를 사용한 동일한 정의입니다.
case class E[A, -B](a: A)// class E
E(2) // E[Int, Any] = E(2)
그러므로 우리는 그것이 반 변형 일 때와 다른 방식으로 똑같은 것을 가지고 있으며 모든 행동이나 추론 규칙을 일관되게 만듭니다. 그러나 이것에 대한 합리적은 확실하지 않습니다 ....
반대의 규칙, 즉 Any
Co-Variant / Invariant와 Nothing
Contra-Variant를 추론하지 않는 이유는 무엇 입니까?
EDIT2
@slouc Answer에 비추어 볼 때 컴파일러가 수행하는 작업과 이유를 여전히 이해하고 있습니다. 아래 예는 내 혼란을 설명합니다.
val myleft = Left("Error") // Left[String,Nothing] = Left(Error)
myleft map { (e:Int) => e * 4} // Either[String,Int] = Left(Error)
- 먼저 컴파일러 는 @slouc의 결론을 재사용하기 위해 "확실하게 작동하는"것으로 유형을 수정 합니다 (함수 컨텍스트에서 더 의미가 있음).
Left[String,Nothing]
- 다음으로 컴파일
myleft
은 Either [String, Int] 유형이라고 추론 합니다.
지도 정의 주어 def map[B](f: A => B): Either[E, B]
, (e:Int) => e * 4
경우에만 제공 할 수 있습니다 myleft
실제로 Left[String,Int]
나Either[String,Int]
즉, 제 질문은 Nothing
나중에 변경 하려는 경우 유형을 수정하는 요점이 무엇인지입니다 .
실제로 다음은 컴파일되지 않습니다.
val aleft: Left[String, Nothing] = Left[String, Int]("Error")
type mismatch;
found : scala.util.Left[String,Int]
required: Left[String,Nothing]
val aleft: Left[String, Nothing] = Left[String, Int]("Error")
그래서 내가 유형을 추론하는 이유는 일반적으로 해당 유형의 변수에 대해 다른 작업을 수행하는 것을 차단하고 ( 하지만 추론 측면에서 확실히 작동합니다 ) 궁극적으로 해당 유형을 변경하여 그 변수로 무언가를 할 수 있습니다 추론 된 유형.
편집 3
Edit2는 약간의 오해이며 모든 것이 @slouc 답변과 댓글로 명확 해졌습니다.
답변
공분산 :
유형F[+A]
및 관계가 주어지면A <: B
다음이 유지됩니다.F[A] <: F[B]
Contravariance :
주어진 유형F[-A]
과 관계A <: B
는 다음과 같습니다.F[A] >: F[B]
컴파일러가 정확한 유형을 유추 할 수없는 경우 공분산의 경우 가능한 가장 낮은 유형을 확인하고 반공 분산의 경우 가능한 가장 높은 유형을 분석합니다.
왜?
이것은 하위 유형의 분산과 관련하여 매우 중요한 규칙입니다. Scala의 다음 데이터 유형의 예에서 볼 수 있습니다.
trait Function1[Input-, Output+]
일반적으로 유형이 함수 / 메소드 매개 변수에 배치되면 소위 "반 변적 위치"에 있음을 의미합니다. 함수 / 메소드 반환 값에 사용되는 경우 소위 "공변 위치"에 있습니다. 둘 다 있으면 불변입니다.
이제이 게시물 시작 부분의 규칙을 고려할 때 다음과 같은 결론을 내립니다.
trait Food
trait Fruit extends Food
trait Apple extends Fruit
def foo(someFunction: Fruit => Fruit) = ???
우리는 공급할 수 있습니다
val f: Food => Apple = ???
foo(f)
함수 f
는 다음과 같은 someFunction
이유로 유효한 대체입니다 .
Food
Fruit
(입력의 반 변성)의 상위 유형입니다.Apple
Fruit
(출력의 공분산)의 하위 유형입니다.
우리는 이것을 다음과 같이 자연어로 설명 할 수 있습니다.
"방법은
foo
을 할 수있는 기능이 필요Fruit
하고 생산을Fruit
이 수단.foo
몇 가지있을 것이다Fruit
그것은에 공급, 일부 기대할 수있는 기능이 필요합니다Fruit
다시 그 기능을 얻을합니다.Food => Apple
모든 것이 괜찮 - 여전히 그것을 먹을 수Fruit
( 기능이 어떤 음식을 취하기 때문입니다) 그리고받을 수 있습니다Fruit
(사과는 과일이므로 계약이 존중됩니다).
초기 딜레마로 돌아와서, 이것이 추가 정보없이 컴파일러가 공변 유형에 대해 가능한 가장 낮은 유형과 반반적인 유형에 대해 가능한 가장 높은 유형에 의존하는 이유를 설명합니다. 에 함수를 제공하려면 foo
확실히 작동 하는 함수가 Any => Nothing
있습니다..
일반적인 분산 .
Scala 문서의 차이 .
Scala의 분산에 관한 기사 (전체 공개 : 내가 썼습니다).
편집하다:
당신을 헷갈리는 게 뭔지 알아요.
를 인스턴스화 할 때 Left[String, Nothing]
나중에 map
함수 Int => Whatever
, 또는 String => Whatever
, 또는을 사용하여 인스턴스화 할 수 있습니다 Any => Whatever
. 이는 앞에서 설명한 함수 입력의 반 변성 때문입니다. 그것이 당신이 map
일하는 이유 입니다.
"나중에 변경할 경우 유형을 Nothing으로 고정하는 이유는 무엇입니까?"
Nothing
반 변성의 경우 알 수없는 유형을 수정하는 컴파일러에 대해 머리를 감싸는 것이 약간 어렵다고 생각합니다 . Any
공분산의 경우 알 수없는 유형을 로 고정하면 더 자연스럽게 느껴집니다 ( "Anything"일 수 있음). 앞에서 설명한 공분산과 반공 변성의 이중성으로 인해 동일한 추론이 반 변성 Nothing
및 공 변성에 적용됩니다 Any
.
이것은 Eugene Burmako의 Unification of Compile-Time and Runtime Metaprogramming in Scala에서 인용 한 것입니다.
https://infoscience.epfl.ch/record/226166 (95-96 쪽)
유형 추론 중에 유형 검사기는 유형 매개 변수의 경계, 용어 인수 유형 및 암시 적 검색 결과에서도 누락 된 유형 인수에 대한 제약 조건을 수집합니다 (Scala가 기능적 종속성의 아날로그를 지원하기 때문에 유형 추론은 암시 적 검색과 함께 작동합니다). 이러한 제약 조건은 알 수없는 형식 인수가 형식 변수로 표시되고 하위 형식 관계에 의해 순서가 부과되는 부등식 시스템으로 볼 수 있습니다.
제약 조건을 수집 한 후 typechecker는 각 단계에서 특정 변형을 부등식에 적용하여 동등하지만 더 단순한 부등식 시스템을 만드는 단계별 프로세스를 시작합니다. 유형 추론의 목표는 원래의 부등식을 원래 시스템의 고유 한 솔루션을 나타내는 등식으로 변환하는 것입니다.
대부분의 경우 유형 추론이 성공합니다. 이 경우 누락 된 형식 인수는 솔루션이 나타내는 형식으로 유추됩니다.
그러나 때때로 유형 추론이 실패합니다. 예를 들어, 입력 파라미터는 경우
T
에있어서의 용어 파라미터 미사용 팬텀, 즉이고, 부등식 시스템 내의 유일한 엔트리 것L <: T <: U
, 여기서L
및U
그것의 상부 및 하부에 각각 결합된다. 이면L != U
이 부등식에 고유 한 솔루션이 없으며 이는 유형 추론의 실패를 의미합니다.유형 추론이 실패하면, 즉 더 이상 변환 단계를 수행 할 수없고 작업 상태에 여전히 약간의 불평등이 포함되어있는 경우 유형 검사기가 교착 상태를 중단합니다. 아직 추론되지 않은 유형 인수, 즉 변수가 여전히 부등식으로 표현되는 인수를 취하고 강제로 최소화 합니다. 즉, 하한과 동일합니다. 이것은 일부 유형 인수가 정확하게 추론되고 일부는 임의의 유형으로 대체되는 결과를 생성합니다. 예를 들어, 제한되지 않은 유형 매개 변수는
Nothing
스칼라 초보자에게 일반적인 혼동의 원인 인 으로 추론됩니다 .
Scala의 유형 추론에 대해 자세히 알아볼 수 있습니다.
지역 유형 추론을 해독하는 Hubert Plociniczak https://infoscience.epfl.ch/record/214757
Guillaume Martres Scala 3, 유형 추론과 당신! https://www.youtube.com/watch?v=lMvOykNQ4zs
Guillaume Martres Dotty 및 유형 : 지금까지의 이야기 https://www.youtube.com/watch?v=YIQjfCKDR5A
슬라이드 http://guillaume.martres.me/talks/
Dotty의 Aleksander Boruch- Gruszecki GADT https://www.youtube.com/watch?v=VV9lPg3fNl8