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 A
is) 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
대한 추가 제한 을 부과하고 있습니다 . 즉, 당신은 단지 호출 할 수 켜짐 . 이 제약 조건은 컴파일 타임에 적용되며 멋지다!A
Foo
getStringLength
Foo[String]
<:<
와 <%<
비슷하게 작동하지만, 약간의 변형과 :
A =:= B
A가 정확히 B 여야 함을 의미A <:< B
는 A가 B의 하위 유형이어야 함을 의미합니다 ( 단순 유형 제약 조건 과 유사<:
)A <%< B
의미 A는 암시 적 변환을 통해 B 로 볼 수 있어야 함을 의미합니다 (단순한 유형 제약 조건과 유사<%
)
@retronym 의이 스 니펫 은 이러한 종류의 작업을 수행하는 방법과 일반화 된 유형 제약 조건이 이제 더 쉬워지는 방법에 대한 좋은 설명입니다.
추가
당신의 후속 질문에 대답하기 위해, 내가 준 예는 꽤 고안되었으며 분명히 유용하지 않습니다. 그러나 그것을 사용하여 List.sumInts
정수 목록을 추가 하는 메소드 와 같은 것을 정의한다고 상상해보십시오 . 이 메소드를 이전의 List
a, 단지 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]
. 형식 제약 조건을 사용하면 모든 것이 좋습니다. 당신이 사용할 수있는 orNull
온 Option[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
컬렉션에서 다른 예제를 찾을 수 있습니다.