[scala] Scala 2.8에서 <: <, <% <및 = : =은 무엇을 의미하며 어디에 기록되어 있습니까?

Predef 의 API 문서 에서 일반 함수 유형 (From) => To의 서브 클래스 임을 알 수 있지만 그게 전부입니다. 음? 어딘가에 문서가있을 수 있지만 검색 엔진은 “<: <“와 같은 “이름”을 잘 처리하지 못하므로 찾을 수 없었습니다.

후속 질문 :이 펑키 기호 / 클래스를 언제 사용해야합니까? 왜?



답변

이를 일반화 된 유형 제한 조건 이라고 합니다 . 타입 매개 변수화 된 클래스 나 특성 내에서 타입 매개 변수 중 하나 를 추가로 제한 있습니다. 예를 들면 다음과 같습니다.

case class Foo[A](a:A) { // 'A' can be substituted with any type
    // getStringLength can only be used if this is a Foo[String]
    def getStringLength(implicit evidence: A =:= String) = a.length
}

암시 적 인수 evidence는 컴파일러에서 제공합니다 (iff Ais) String. 당신은 그것을 생각할 수있는 증거A 입니다 String고마웠다 인수 자체 만이 존재하는 것을 알고, 중요하지 않습니다. [편집 : 글쎄요, 기술적으로 실제로는 중요합니다.에서 (으) A로의 암시 적 변환을 나타내므로 컴파일러가 String호출 a.length하지 않아도됩니다.]

이제 다음과 같이 사용할 수 있습니다.

scala> Foo("blah").getStringLength
res6: Int = 4

그러나 내가 Foo다른 것을 포함하는 것으로 사용하려고 하면 String:

scala> Foo(123).getStringLength
<console>:9: error: could not find implicit value for parameter evidence: =:=[Int,String]

“Int == String이라는 증거를 찾을 수 없습니다”라는 오류를 읽을 수 있습니다. 일반적으로 요구 되는 것보다 유형에 getStringLength대한 추가 제한 을 부과하고 있습니다 . 즉, 당신은 단지 호출 할 수 켜짐 . 이 제약 조건은 컴파일 타임에 적용되며 멋지다!AFoogetStringLengthFoo[String]

<:<<%<비슷하게 작동하지만, 약간의 변형과 :

  • A =:= B A가 정확히 B 여야 함을 의미
  • A <:< B는 A가 B의 하위 유형이어야 함을 의미합니다 ( 단순 유형 제약 조건 과 유사 <:)
  • A <%< B의미 A는 암시 적 변환을 통해 B 로 볼 수 있어야 함을 의미합니다 (단순한 유형 제약 조건과 유사 <%)

@retronym 의이 스 니펫 은 이러한 종류의 작업을 수행하는 방법과 일반화 된 유형 제약 조건이 이제 더 쉬워지는 방법에 대한 좋은 설명입니다.

추가

당신의 후속 질문에 대답하기 위해, 내가 준 예는 꽤 고안되었으며 분명히 유용하지 않습니다. 그러나 그것을 사용하여 List.sumInts정수 목록을 추가 하는 메소드 와 같은 것을 정의한다고 상상해보십시오 . 이 메소드를 이전의 Lista, 단지 a 에서 호출하는 것을 허용하지 않으려 고 합니다 List[Int]. 그러나 List형식 생성자는 그렇게 제한 될 수 없습니다. 여전히 문자열, foos, bar 및 notnots 목록을 가질 수 있기를 원합니다. 따라서 일반화 된 형식 제약 조건을 설정 sumInts하면 해당 메서드 에만에 사용할 수있는 추가 제약 조건이 있는지 확인할 수 있습니다 List[Int]. 기본적으로 특정 종류의 목록에 대한 특수 사례 코드를 작성하고 있습니다.


답변

완전한 대답은 아닙니다 (다른 사람들은 이미 이것에 대답했습니다). 구문을 더 잘 이해하는 데 도움이 될 수있는 다음 사항에 유의하고 싶었습니다.

def getStringLength(implicit evidence: A =:= String)

유형 연산자 에 Scala의 대체 접두사 구문을 사용합니다 .

따라서, A =:= String동일합니다 =:=[A, String](그리고 =:=멋진 이름을 가진 클래스 또는 특성 일뿐입니다). 이 구문은 “일반”클래스에서도 작동합니다. 예를 들어 다음과 같이 작성할 수 있습니다.

val a: Tuple2[Int, String] = (1, "one")

이처럼 :

val a: Int Tuple2 String = (1, "one")

메소드 호출에 대한 “정상”with .()연산자 구문 의 두 구문과 유사 합니다.


답변

이 구성이 무엇인지 이해하려면 다른 답변을 읽으십시오. 여기가 언제 야 당신이 그들을 사용해야합니다. 특정 유형에 대해서만 메소드를 제한해야 할 때 사용합니다.

다음은 예입니다. 다음과 같이 동종 쌍을 정의한다고 가정하십시오.

class Pair[T](val first: T, val second: T)

이제 다음 smaller과 같이 메소드를 추가하려고합니다 .

def smaller = if (first < second) first else second

T주문한 경우에만 작동합니다 . 전체 수업을 제한 할 수 있습니다.

class Pair[T <: Ordered[T]](val first: T, val second: T)

그러나 그것은 부끄러운 것 같습니다- T주문하지 않으면 수업에 사용할 수 있습니다 . 형식 제약 조건을 사용하여 smaller메서드를 계속 정의 할 수 있습니다 .

def smaller(implicit ev: T <:< Ordered[T]) = if (first < second) first else second

전화하지 않는Pair[File], 인스턴스화해도 괜찮습니다. smaller 그것을.

의 경우 Option구현자는 orNull이해가되지 않더라도 메소드를 원했습니다 Option[Int]. 형식 제약 조건을 사용하면 모든 것이 좋습니다. 당신이 사용할 수있는 orNullOption[String], 당신은을 형성 할 수있는 Option[Int]당신이 전화하지 않는 한, 그것은을 사용할 orNull그것을. 시도 Some(42).orNull하면 매력적인 메시지가 나타납니다.

 error: Cannot prove that Null <:< Int


답변

사용되는 위치에 따라 다릅니다. 암시 적 매개 변수 유형을 선언하는 동안 사용되는 경우가 대부분 클래스입니다. 드물게 객체 일 수도 있습니다. 마지막으로 이들은 Manifest객체의 연산자가 될 수 있습니다 . 그것들은 scala.Predef특별히 잘 문서화되어 있지는 않지만 처음 두 경우에 내부적으로 정의됩니다 .

그들은, 클래스 사이의 관계를 테스트 단지 같은 방법 제공하기 위해 의미 <:하고 <%후자를 사용할 수없는 경우 상황에서 수행합니다.

“언제 사용해야합니까?”라는 질문에 대해, 당신이 알아야 할 것이 아니라면 대답은하지 말아야합니다. 🙂 편집 : 좋아, 좋아, 여기 라이브러리의 예가 있습니다. 에 Either, 당신은 :

/**
  * Joins an <code>Either</code> through <code>Right</code>.
  */
 def joinRight[A1 >: A, B1 >: B, C](implicit ev: B1 <:< Either[A1, C]): Either[A1, C] = this match {
   case Left(a)  => Left(a)
   case Right(b) => b
 }

 /**
  * Joins an <code>Either</code> through <code>Left</code>.
  */
 def joinLeft[A1 >: A, B1 >: B, C](implicit ev: A1 <:< Either[C, B1]): Either[C, B1] = this match {
   case Left(a)  => a
   case Right(b) => Right(b)
 }

Option, 당신은 :

def orNull[A1 >: A](implicit ev: Null <:< A1): A1 = this getOrElse null

컬렉션에서 다른 예제를 찾을 수 있습니다.


답변