형식 매개 변수의 형식 인수를 유추하는 기술 이름입니까?


9

설정 :Iterator 유형 매개 변수가있는 유형이 있다고 가정합니다 Element.

interface Iterator<Element> {}

그런 다음을 Iterable반환하는 하나의 메소드 가있는 인터페이스 가 있습니다 Iterator.

// T has an upper bound of Iterator
interface Iterable<T: Iterator> {
    getIterator(): T
}

Iterator일반적인 문제 는 형식 인수를 제공해야한다는 것입니다.

이를 해결하기위한 한 가지 아이디어는 반복자의 유형을 "추론"하는 것입니다. 다음 의사 코드 Element는 유형 인수로 추정되는 유형 변수가 있다는 아이디어를 표현합니다 Iterator.

interface <Element> Iterable<T: Iterator<Element>> {
    getIterator(): T
}

그리고 우리는 이것을 다음과 같이 사용합니다 :

class Vec<Element> implements Iterable<VecIterator<Element>> {/*...*/}

이 정의는 그 정의에서 다른 곳에서는 Iterable사용하지 Element않지만 실제 사용 사례는 사용합니다. Iterable또한 사용하는 특정 함수 Iterable는 양방향 반복기와 같은 특정 반복자 만 반환 하는 매개 변수를 제한 할 수 있어야하므로 반환 된 반복자가 요소 유형 대신 매개 변수화되는 이유입니다.


질문 :

  • 이러한 유추 된 유형 변수에 대해 확립 된 이름이 있습니까? 기술 전체는 어떻습니까? 특정 명명법을 모르면 이러한 예를 야생에서 검색하거나 언어 별 기능에 대해 배우기가 어려워졌습니다.
  • 제네릭이 포함 된 모든 언어에이 기술이있는 것은 아닙니다. 이 언어들에서 유사한 기술의 이름 이 있습니까?

1
컴파일하지 않으려는 컴파일되지 않은 코드를 보여줄 수 있습니까? 나는 그것이 질문을 더 명확하게 할 것이라고 생각합니다.
스위퍼

1
하나의 언어를 선택할 수도 있습니다 (또는 사용중인 언어 및 구문의 의미를 말하십시오). 예를 들어 C #은 아닙니다. 인터넷에서 사용 가능한 "유형 추론"에 대한 많은 정보가 있지만 여기에 적용되는지 확실하지 않습니다.

5
한 언어로 코드를 컴파일하려고하지 않고 언어로 제네릭을 구현하고 있습니다. 또한 명명 및 디자인 문제이기도합니다. 이것이 다소 무의미한 이유입니다. 용어를 모르면 기존 언어로 된 예제와 설명서를 찾기가 어렵습니다. 분명히 이것은 독특한 문제가 아닌가?
Levi Morrison

답변:


2

이 문제에 대한 특정 용어가 있는지 모르겠지만 세 가지 일반적인 솔루션 클래스가 있습니다.

  • 동적 디스패치에 유리한 콘크리트 유형을 피하십시오
  • 형식 제약 조건에 자리 표시 자 형식 매개 변수 허용
  • 연관된 타입 / 타입 패밀리를 사용하여 타입 파라미터를 피하십시오

그리고 물론 기본 해결책 : 모든 매개 변수를 계속 철자하십시오.

콘크리트 유형을 피하십시오.

Iterable인터페이스를 다음과 같이 정의했습니다 .

interface <Element> Iterable<T: Iterator<Element>> {
    getIterator(): T
}

이를 통해 인터페이스의 사용자는 정확한 구체적인 유형 T의 반복자 를 얻을 수 있기 때문에 최대 전력을 제공 합니다. 또한 컴파일러는 인라인과 같은 더 많은 최적화를 적용 할 수 있습니다.

그러나 Iterator<E>동적으로 전달 된 인터페이스 인 경우 구체적 유형을 알 필요는 없습니다. 예를 들어 Java가 사용하는 솔루션입니다. 인터페이스는 다음과 같이 작성됩니다.

interface Iterable<Element> {
    getIterator(): Iterator<Element>
}

이것의 흥미로운 변형은 Rust의 impl Trait구문 입니다.이 구문은 추상 반환 유형으로 함수를 선언 할 수 있지만 호출 유형에서 구체적인 유형을 알 수 있으므로 최적화가 가능합니다. 이것은 암시 적 유형 매개 변수와 유사하게 작동합니다.

자리 표시 자 유형 매개 변수를 허용합니다.

Iterable인터페이스는이로 쓸 수있을 때문에, 요소의 형태에 대해 알 필요가 없다 :

interface Iterable<T: Iterator<_>> {
    getIterator(): T
}

어디 T: Iterator<_>제약 조건을 표현하는 "T 관계없이 요소 유형의 반복자입니다." "어떤 종류의 존재로 : 더 엄격하게, 우리는이를 표현할 수있는 Element그 있도록 TIterator<Element>에 대한 구체적인 유형을 알 필요없이," Element. 이는 type-expression Iterator<_>이 실제 유형을 설명하지 않으며 유형 제한 조건으로 만 사용할 수 있음을 의미합니다.

유형 패밀리 / 관련 유형을 사용하십시오.

예를 들어 C ++에서 형식에는 형식 멤버가있을 수 있습니다. 이것은 표준 라이브러리 전체에서 일반적으로 사용됩니다 (예 :) std::vector::value_type. 모든 시나리오에서 유형 매개 변수 문제를 실제로 해결하는 것은 아니지만 유형이 다른 유형을 참조 할 수 있으므로 단일 유형 매개 변수는 전체 유형의 관련 유형을 설명 할 수 있습니다.

정의하자 :

interface Iterator {
  type ElementType
  fn next(): ElementType
}

interface Iterable {
  type IteratorType: Iterator
  fn getIterator(): IteratorType
}

그때:

class Vec<Element> implement Iterable {
  type IteratorType = VecIterator<Element>
  fn getIterator(): IteratorType { ... }
}

class VecIterator<T> implements Iterator {
  type ElementType = T
  fn next(): ElementType { ... }
}

이것은 매우 유연 해 보이지만 유형 제약을 표현하기가 더 어려울 수 있습니다. 예를 들어 작성된대로 Iterable반복자 요소 유형을 강요하지 않고 interface Iterator<T>대신 선언 할 수 있습니다 . 그리고 지금 당신은 상당히 복잡한 유형의 미적분학을 다루고 있습니다. 실수로 그러한 유형의 시스템을 결정할 수 없게 만드는 것은 매우 쉽습니다 (또는 이미 있을까요?).

연관된 유형은 유형 매개 변수의 기본값으로 매우 편리 할 수 ​​있습니다. 예를 들어 Iterable인터페이스에 요소 유형에 대해 별도의 유형 매개 변수가 필요하지만 일반적으로 반복자 요소 유형과 동일하지는 않지만 자리 표시 자 유형 매개 변수가 있다고 가정하면 다음과 같이 말할 수 있습니다.

interface Iterable<T: Iterator<_>, Element = T::Element> {
  ...
}

그러나 이것은 언어 인체 공학적 기능 일 뿐이며 언어를 더 강력하게 만들지는 않습니다.


타입 시스템은 어렵 기 때문에 다른 언어에서 작동하는 것과 작동하지 않는 것을 살펴 보는 것이 좋습니다.

예를 들어 Rust Book 의 Advanced Traits 장을 읽고 관련 유형에 대해 설명하십시오. 그러나 제네릭 대신 관련 유형을 선호하는 일부 포인트는 언어에 하위 유형 지정 기능이 없으며 각 특성은 유형 당 최대 한 번만 구현할 수 있기 때문에 적용됩니다. 즉 Rust 특성은 Java와 유사한 인터페이스가 아닙니다.

다른 흥미로운 유형 시스템에는 다양한 언어 확장 기능이있는 Haskell이 있습니다. OCaml 모듈 / 펑 터는 객체 또는 매개 변수화 된 유형과 직접 통합하지 않고 비교적 일반 유형의 유형 패밀리입니다. Java는 유형 시스템에 제한이 있습니다 (예 : 유형이 삭제 된 제네릭, 값 유형에 대한 제네릭 없음). C #은 Java와 매우 유사하지만 구현 복잡성이 증가함에 따라 이러한 제한을 대부분 피할 수 있습니다. Scala는 C # 스타일 제네릭을 Java 플랫폼 위에 Haskell 스타일 타입 클래스와 통합하려고합니다. C ++의 믿을 수 없을 정도로 간단한 템플릿은 잘 연구되었지만 대부분의 제네릭 구현과는 다릅니다.

또한 이러한 언어의 표준 라이브러리 (특히 목록이나 해시 테이블과 같은 표준 라이브러리 컬렉션)를보고 어떤 패턴이 일반적으로 사용되는지 확인할 가치가 있습니다. 예를 들어 C ++에는 다양한 반복 기능이있는 복잡한 시스템이 있으며 Scala는 세밀한 수집 기능을 특성으로 인코딩합니다. Java 표준 라이브러리 인터페이스는 간혹 소리가 나지 Iterator#remove()않지만 중첩 클래스를 일종의 관련 유형 (예 :)으로 사용할 수 있습니다 Map.Entry.

당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.