스칼라는 암시 적 위치를 어디에서 찾습니까?


398

암시 스칼라 이민자에 대한 질문은 것 같다 : 어디 implicits에 대한 컴파일러의 모습입니까? 나는 질문이 전혀없는 것처럼 질문이 완전히 형성되는 것처럼 보이지 않기 때문에 암시 적 의미입니다. :-) 예를 들어 integral아래 값은 어디에서 오는가?

scala> import scala.math._
import scala.math._

scala> def foo[T](t: T)(implicit integral: Integral[T]) {println(integral)}
foo: [T](t: T)(implicit integral: scala.math.Integral[T])Unit

scala> foo(0)
scala.math.Numeric$IntIsIntegral$@3dbea611

scala> foo(0L)
scala.math.Numeric$LongIsIntegral$@48c610af

첫 번째 질문에 대한 답을 배우기로 결정한 사람들이 따라야 할 또 다른 질문은 명백한 모호성이있는 특정 상황에서 컴파일러가 사용하는 암시 적을 어떻게 선택합니까?

예를 들어 하나에서 다른로 scala.Predef두 가지 변환을 정의 합니다 . 그러나 두 클래스 모두 많은 메소드를 공유하므로 Scala가 호출 할 때 모호함에 대해 불평하지 않는 이유는 무엇입니까?StringWrappedStringStringOpsmap

참고 : 이 질문은 더 일반적인 방식으로 문제를 진술하기 위해이 다른 질문 에서 영감을 얻었습니다 . 답변에서 참조되었으므로 예제가 거기에서 복사되었습니다.

답변:


554

암시 적 유형

스칼라의 내포는 "자동"으로 전달 될 수있는 값을 말하거나 한 유형에서 다른 유형으로 자동 변환되는 값을 말합니다.

암시 적 변환

하나는 메소드를 호출하는 경우, 후자의 유형에 대한 매우 간략하게 말하기 m객체에 대한 o클래스를 C, 그 클래스가 지원 방법을하지 않습니다 m, 다음 스칼라에서 암시 적 변환을 찾을 것입니다 C뭔가 수행 지원 m. 간단한 예제는 방법이 될 것이다 mapString:

"abc".map(_.toInt)

String방법을 지원하지 않습니다 map만, StringOps수행, 그리고에서 암시 적 변환 거기 StringStringOps(참조 가능 implicit def augmentStringPredef).

암시 적 매개 변수

다른 종류의 암시 적은 암시 적 매개 변수 입니다. 이것들은 다른 매개 변수와 마찬가지로 메서드 호출에 전달되지만 컴파일러는 자동으로 채우기를 시도합니다. 그렇게 할 수 없다면 불평 할 것입니다. 하나는 하나를 사용하는 방법이다, 명시 적으로 이러한 매개 변수를 전달 breakOut(예를 들어,에 대한 질문을보고 breakOut당신이 도전까지 기분이 하루에).

이 경우 foo메소드 선언 과 같이 암시 적 필요성을 선언해야합니다 .

def foo[T](t: T)(implicit integral: Integral[T]) {println(integral)}

경계보기

암시 적이 암시 적 변환과 암시 적 매개 변수 둘 다인 상황이 있습니다. 예를 들면 다음과 같습니다.

def getIndex[T, CC](seq: CC, value: T)(implicit conv: CC => Seq[T]) = seq.indexOf(value)

getIndex("abc", 'a')

getIndex클래스에서로 사용할 수있는 암시 적 변환이있는 한이 메서드 는 모든 개체를받을 수 있습니다 Seq[T]. 그 때문에 a String를 전달 getIndex하면 작동합니다.

내부적으로 컴파일러는 변화 seq.IndexOf(value)conv(seq).indexOf(value).

이것은 쓸만한 구문 설탕이 있기에 매우 유용합니다. 이 구문 설탕을 사용하여 다음 getIndex과 같이 정의 할 수 있습니다.

def getIndex[T, CC <% Seq[T]](seq: CC, value: T) = seq.indexOf(value)

이는 문법적으로는 기재되어 결합도 유사한, 상한 ( CC <: Seq[Int]) 또는 하한 ( T >: Null).

컨텍스트 바운드

암시 적 매개 변수의 또 다른 일반적인 패턴은 유형 클래스 패턴 입니다. 이 패턴을 사용하면 선언하지 않은 클래스에 공통 인터페이스를 제공 할 수 있습니다. 브리지 패턴과 관심사 분리를위한 어댑터 및 어댑터 패턴의 역할을 모두 수행 할 수 있습니다.

Integral당신이 언급 한 클래스 타입 클래스 패턴의 전형적인 예이다. 스칼라 표준 라이브러리의 또 다른 예는 Ordering입니다. 이 패턴을 많이 사용하는 라이브러리 인 Scalaz가 있습니다.

다음은 그 사용 예입니다.

def sum[T](list: List[T])(implicit integral: Integral[T]): T = {
    import integral._   // get the implicits in question into scope
    list.foldLeft(integral.zero)(_ + _)
}

컨텍스트 바운드 (context bound) 라고 불리는 구문 설탕도 암시 적을 참조 할 필요성이 덜 유용합니다. 해당 방법을 직접 변환하면 다음과 같습니다.

def sum[T : Integral](list: List[T]): T = {
    val integral = implicitly[Integral[T]]
    import integral._   // get the implicits in question into scope
    list.foldLeft(integral.zero)(_ + _)
}

컨텍스트 바운드는이 를 사용하는 다른 메소드 로 전달 해야 할 때 더 유용 합니다. 예를 들어, sortedon 메소드 Seq는 내재적이어야합니다 Ordering. 메소드를 작성하기 위해 다음을 작성할 reverseSort수 있습니다.

def reverseSort[T : Ordering](seq: Seq[T]) = seq.sorted.reverse

때문에 Ordering[T]암시에 전달 reverseSort, 그 다음에 암시 적으로 전달할 수 있습니다 sorted.

내재는 어디에서 왔습니까?

컴파일러가 객체 클래스에 존재하지 않는 메서드를 호출하거나 암시 적 매개 변수가 필요한 메서드를 호출하기 때문에 암시 적 필요성이 확인되면 필요에 맞는 암시 적을 검색합니다. .

이 검색은 어떤 암시 적이 보이고 어떤 것이 보이지 않는지를 정의하는 특정 규칙을 따릅니다. 컴파일러가 암시 적을 검색 할 위치를 보여주는 다음 표는 Josh Suereth의 암시 적에 대한 훌륭한 프레젠테이션 에서 발췌 한 것으로 Scala 지식을 향상시키려는 모든 사람에게 진심으로 권장합니다. 그 이후로 피드백과 업데이트로 보완되었습니다.

아래 1 번 항목에서 사용 가능한 암시 적은 2 번 항목보다 우선합니다. 그 외에, 암시 적 매개 변수의 유형과 일치하는 적합한 인수가 여러 개있는 경우 정적 오버로드 해결 규칙을 사용하여 가장 구체적인 인수가 선택됩니다 (스칼라 참조). 사양 §6.26.3). 더 자세한 정보는이 답변의 끝에 링크 된 질문에서 찾을 수 있습니다.

  1. 현재 범위에서 먼저 살펴보기
    • 현재 범위에 정의 된 암시 적
    • 명시 적 수입
    • 와일드 카드 수입
    • 다른 파일에서 동일한 범위
  2. 이제 관련 유형을 살펴보십시오.
    • 유형의 컴패니언 객체
    • 인수 유형의 암시 적 범위 (2.9.1)
    • 암시 적 유형 인수 범위 (2.8.0)
    • 중첩 유형의 외부 객체
    • 다른 치수

그들에게 몇 가지 예를 들어 보자.

현재 범위에서 정의 된 암시 적

implicit val n: Int = 5
def add(x: Int)(implicit y: Int) = x + y
add(5) // takes n from the current scope

명시 적 수입

import scala.collection.JavaConversions.mapAsScalaMap
def env = System.getenv() // Java map
val term = env("TERM")    // implicit conversion from Java Map to Scala Map

와일드 카드 수입

def sum[T : Integral](list: List[T]): T = {
    val integral = implicitly[Integral[T]]
    import integral._   // get the implicits in question into scope
    list.foldLeft(integral.zero)(_ + _)
}

다른 파일에서 동일한 범위

편집 : 이것은 다른 우선 순위가없는 것 같습니다. 우선 순위 구분을 보여주는 예가있는 경우 의견을 작성하십시오. 그렇지 않으면, 이것에 의존하지 마십시오.

이것은 첫 번째 예와 같지만 내재적 정의가 사용법과 다른 파일에 있다고 가정합니다. 패키지 객체를 사용하여 암시 적 방법을 참조하십시오 .

유형의 동반 객체

여기에 주목할 두 가지 객체 동반자가 있습니다. 먼저 "소스"유형의 객체 동반자를 살펴 봅니다. 예를 들어 객체 내부에 Option로의 암시 적 변환이 Iterable있으므로에 Iterable메소드를 호출 하거나을 (를) 기대하는 무언가를 Option전달할 OptionIterable있습니다. 예를 들면 다음과 같습니다.

for {
    x <- List(1, 2, 3)
    y <- Some('x')
} yield (x, y)

그 표현은 컴파일러에 의해

List(1, 2, 3).flatMap(x => Some('x').map(y => (x, y)))

그러나 List.flatMap기대 TraversableOnce하는 Option없습니다. 컴파일러는 내부 모습 Option의 객체 동반자와의 변환 발견 Iterable이다, TraversableOnce이 표현이 정확하기를.

둘째, 예상 유형의 컴패니언 개체 :

List(1, 2, 3).sorted

이 메서드 sorted는 암시 적 Ordering입니다. 이 경우 객체 내부를 살펴보고 Ordering클래스 Ordering와 동반 하여 암시 적 위치를 찾습니다 Ordering[Int].

수퍼 클래스의 컴패니언 객체도 살펴 봅니다. 예를 들면 다음과 같습니다.

class A(val n: Int)
object A { 
    implicit def str(a: A) = "A: %d" format a.n
}
class B(val x: Int, y: Int) extends A(y)
val b = new B(5, 2)
val s: String = b  // s == "A: 2"

이것이 Scala 가 내부 에서 발견되는 것처럼 암시 Numeric[Int]적이고 Numeric[Long]귀하의 질문에서 발견 한 방법 입니다.NumericIntegral

인수 유형의 암시 적 범위

인수 type이있는 메소드가 있으면 A암시 적 유형의 범위 A도 고려됩니다. "암시 적 범위"는 이러한 모든 규칙이 재귀 적으로 적용됨을 의미합니다. 예를 들어 A위의 규칙에 따라 동반 객체 에서 암시 적을 검색합니다.

이는 A해당 매개 변수의 변환에 대한 암시 적 범위 가 검색되는 것이 아니라 전체 표현식에 대한 검색을 의미합니다 . 예를 들면 다음과 같습니다.

class A(val n: Int) {
  def +(other: A) = new A(n + other.n)
}
object A {
  implicit def fromInt(n: Int) = new A(n)
}

// This becomes possible:
1 + new A(1)
// because it is converted into this:
A.fromInt(1) + new A(1)

Scala 2.9.1부터 사용할 수 있습니다.

형식 인수의 암시 적 범위

이것은 타입 클래스 패턴이 실제로 작동하기 위해 필요합니다. Ordering예를 들어 다음과 같이 고려하십시오 . 컴패니언 객체에 암시 적 요소가 포함되어 있지만 추가 할 수는 없습니다. 그렇다면 어떻게 Ordering자동으로 찾은 자신 만의 수업을 만들 수 있습니까?

구현부터 시작하겠습니다.

class A(val n: Int)
object A {
    implicit val ord = new Ordering[A] {
        def compare(x: A, y: A) = implicitly[Ordering[Int]].compare(x.n, y.n)
    }
}

따라서 전화 할 때 어떤 일이 발생하는지 고려하십시오

List(new A(5), new A(2)).sorted

우리가 보았 듯이,이 방법 sortedOrdering[A](실제로 Ordering[B]어디를 기대합니다 B >: A)를 기대합니다 . 내부 Ordering에는 그런 것이 없으며 볼 소스 유형이 없습니다. 물론, 내부를 찾는 A형식 인수Ordering.

이것은 다양한 수집 방법이 CanBuildFrom작동 하는 방식이기도합니다 CanBuildFrom.

참고 : Ordering는로 정의되며 trait Ordering[T], 여기서 T유형 매개 변수입니다. 이전에는 스칼라가 유형 매개 변수 내부를 살펴 보았으며 이는별로 의미가 없습니다. 상기 찾았다 암시이다 Ordering[A]여기서, A파라미터를 입력하지 실제 유형 : 이는 인 형식 인수Ordering. 스칼라 사양의 7.2 절을 참조하십시오.

Scala 2.8.0부터 사용할 수 있습니다.

중첩 유형의 외부 객체

나는 실제로 이것의 예를 보지 못했다. 누군가 공유 할 수 있으면 감사하겠습니다. 원칙은 간단합니다.

class A(val n: Int) {
  class B(val m: Int) { require(m < n) }
}
object A {
  implicit def bToString(b: A#B) = "B: %d" format b.m
}
val a = new A(5)
val b = new a.B(3)
val s: String = b  // s == "B: 3"

다른 차원

나는 이것이 농담이었을 것이라고 확신하지만,이 답변은 최신이 아닐 수도 있습니다. 따라서이 문제를 최종 중재인으로 생각하지 마십시오. 오래된 정보가 발견되면 문제를 해결할 수 있도록 알려주십시오.

편집하다

관심있는 관련 질문 :


60
이제 책에서 답을 사용하기 시작할 때가되었습니다. 이제는 그것을 모두 합치면됩니다.
pedrofurla

3
@pedrofurla 나는 포르투갈어로 책을 쓰는 것으로 간주되었습니다. 누군가 기술 출판사와 연락 할 수 있다면 ...
Daniel C. Sobral

2
유형의 파트의 컴패니언의 패키지 오브젝트도 검색됩니다. lampsvn.epfl.ch/trac/scala/ticket/4427
retronym

1
이 경우 암시 적 범위의 일부입니다. 호출 사이트는 해당 패키지 내에있을 필요는 없습니다. 그것은 놀랍습니다.
retronym

2
네, 그래서 stackoverflow.com/questions/8623055 특히,하지만 난 당신이 쓴 눈치 커버 "다음 목록은 우선 순위 순서로 표시하기위한 것입니다 ... 제발보고." 기본적으로 내부 목록은 모두 같은 무게를 갖기 때문에 (2.10 이상) 정렬되지 않아야합니다.
유진 요코타

23

나는 암시 적 매개 변수 해결의 우선 순위를 찾고 싶었을뿐 아니라 수입 세금없이 암시 적을 재검토 하는 블로그 게시물을 작성했습니다 (그리고 피드백 후 암시 적 매개 변수 우선 순위 ).

목록은 다음과 같습니다.

  • 1) 로컬 선언, 가져 오기, 외부 범위, 상속, 접두사없이 액세스 할 수있는 패키지 객체를 통해 현재 호출 범위에 암시 적 표시
  • 2) 암시 적 범위 는 검색하는 암시 적 유형과 관계가있는 모든 종류의 컴패니언 객체 및 패키지 객체를 포함합니다. 해당되는 경우 해당 매개 변수 및 수퍼 타입 ​​및 수퍼 특성).

어느 단계에서든 둘 이상의 암시 적 정적 오버로드 규칙을 사용하여이를 해결합니다.


3
패키지, 객체, 특성 및 클래스를 정의하고 범위를 참조 할 때 문자를 사용하는 코드를 작성하면 개선 될 수 있습니다. 메소드 선언은 전혀 필요하지 않습니다. 이름과 누가 누구를, 어느 범위로 확장 할 수 있습니까?
Daniel C. Sobral
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.