종속 입력이란 무엇입니까?


82

누군가 나에게 종속 타이핑을 설명 할 수 있습니까? Haskell, Cayenne, Epigram 또는 기타 기능적 언어에 대한 경험이 거의 없으므로 사용할 수있는 용어가 간단할수록 더 감사 할 것입니다!


그렇다면 위키피디아 기사에서 정확히 이해하지 못한 것은 무엇입니까?
Karl Knechtel 2012

124
글쎄요, 기사는 람다 큐브로 시작하는데, 저에게는 일종의 양고기처럼 들립니다. 그런 다음 λΠ2 시스템에 대해 논의하고 외계인을 말하지 않기 때문에 해당 섹션을 건너 뛰었습니다. 그런 다음 나는 유도 구조의 미적분에 대해 읽었는데, 우연히 미적분, 열 전달 또는 구조와 거의 관련이없는 것 같습니다. 언어 비교표를 제공 한 후 기사가 끝나고 페이지에 도착했을 때보 다 더 혼란스러워집니다.
Nick

3
@Nick 위키 백과의 일반적인 문제입니다. 몇 년 전에 귀하의 의견을 보았고 그 이후로 기억했습니다. 지금 북마크하고 있습니다.
Daniel H

답변:


111

이것을 고려하십시오 : 모든 괜찮은 프로그래밍 언어에서 함수를 작성할 수 있습니다.

def f(arg) = result

여기에서 f값을 취하고 값 arg을 계산합니다 result. 값에서 값으로의 함수입니다.

이제 일부 언어에서는 다형성 (일명 일반) 값을 정의 할 수 있습니다.

def empty<T> = new List<T>()

여기에서 empty유형을 취하고 T값을 계산합니다. 유형에서 값으로의 함수입니다.

일반적으로 일반 유형 정의를 가질 수도 있습니다.

type Matrix<T> = List<List<T>>

이 정의는 유형을 취하고 유형을 리턴합니다. 유형에서 유형으로의 함수로 볼 수 있습니다.

평범한 언어가 제공하는 것이 너무 많습니다. 네 번째 가능성, 즉 값에서 유형으로 함수를 정의하는 경우 언어를 종속 유형이라고합니다. 즉, 값에 대한 유형 정의 매개 변수화 :

type BoundedInt(n) = {i:Int | i<=n}

일부 주류 언어에는 혼동해서는 안되는 가짜 형태가 있습니다. 예를 들어 C ++에서 템플릿은 값을 매개 변수로 사용할 수 있지만 적용 할 때 컴파일 타임 상수 여야합니다. 진정한 의존형 언어에서는 그렇지 않습니다. 예를 들어 위의 유형을 다음과 같이 사용할 수 있습니다.

def min(i : Int, j : Int) : BoundedInt(j) =
  if i < j then i else j

여기서 함수의 결과 유형 실제 인수 값 j, 즉 용어 에 따라 다릅니다 .


하지만이 BoundedInt예제는 실제로 구체화 유형이 아닙니까? 그것은 '꽤 가깝다'지만, 예를 들어 Idris가 dep.typing에 대한 튜토리얼에서 처음 언급 한 '종속 형'의 종류는 아닙니다.
Narfanar

3
@Noein, 정제 유형은 실제로 단순한 형태의 종속 유형입니다.
Andreas Rossberg 2015 년

21

종속 유형을 사용 하면 컴파일 시간에 더 많은 논리 오류 를 제거 할 수 있습니다 . 이를 설명하기 위해 기능에 대한 다음 사양을 고려하십시오 .f

함수 f짝수 정수만 입력 해야합니다 .

종속 유형이 없으면 다음과 같이 할 수 있습니다.

def f(n: Integer) := {
  if  n mod 2 != 0 then 
    throw RuntimeException
  else
    // do something with n
}

여기서 컴파일러 n는 실제로 짝수 인지 감지 할 수 없습니다 . 즉, 컴파일러의 관점에서 다음 표현식이 괜찮은지 감지 할 수 없습니다 .

f(1)    // compiles OK despite being a logic error!

이 프로그램은 실행되고 런타임에 예외를 발생시킵니다. 즉, 프로그램에 논리 오류가 있습니다.

이제 종속 유형을 사용하면 훨씬 더 많은 표현을 사용할 수 있으며 다음과 같이 작성할 수 있습니다.

def f(n: {n: Integer | n mod 2 == 0}) := {
  // do something with n
}

다음 n은 종속 유형 {n: Integer | n mod 2 == 0}입니다. 이것을 크게 읽는 것이 도움이 될 수 있습니다.

n 각 정수를 2로 나눌 수있는 정수 세트의 구성원입니다.

이 경우 컴파일러는 컴파일 타임에 홀수를 전달한 논리 오류를 감지 f하여 프로그램이 처음에 실행되지 않도록합니다.

f(1)    // compiler error

다음은 이러한 요구 사항을 충족 하는 함수를 구현하는 방법에 대한 Scala 경로 종속 유형 을 사용한 예시입니다 f.

case class Integer(v: Int) {
  object IsEven { require(v % 2 == 0) }
  object IsOdd { require(v % 2 != 0) }
}

def f(n: Integer)(implicit proof: n.IsEven.type) =  { 
  // do something with n safe in the knowledge it is even
}

val `42` = Integer(42)
implicit val proof42IsEven = `42`.IsEven

val `1` = Integer(1)
implicit val proof1IsOdd = `1`.IsOdd

f(`42`) // OK
f(`1`)  // compile-time error

핵심은 값 n이 다음과 같이 값 유형에 어떻게 나타나는지 확인 하는 proof것입니다 n.IsEven.type.

def f(n: Integer)(implicit proof: n.IsEven.type)
      ^                           ^
      |                           |
    value                       value

유형 n.IsEven.type 에 따라 다르 n므로 용어 종속 유형 이라고 말합니다 .


5
임의의 값을 어떻게 처리합니까? 예를 들어, f(random())컴파일 오류가 발생합니까?
Wong Jia Hau

5
f어떤 식에 적용 하려면 컴파일러가 (당신의 도움이 있든 없든)식이 항상 짝수이고 random()(사실 이상 할 수 있기 때문에) 그러한 증명이 존재 f(random())하지 않으므로 컴파일에 실패합니다.
Matthijs dec.

18

C ++를 알게된다면 동기를 부여하는 예제를 쉽게 제공 할 수 있습니다.

컨테이너 유형과 인스턴스 두 개가 있다고 가정 해 보겠습니다.

typedef std::map<int,int> IIMap;
IIMap foo;
IIMap bar;

다음 코드 조각을 고려하십시오 (foo가 비어 있지 않다고 가정 할 수 있음).

IIMap::iterator i = foo.begin();
bar.erase(i);

이것은 명백한 쓰레기입니다 (그리고 아마도 데이터 구조를 손상시킬 것입니다). 그러나 "iterator into foo"와 "iterator into bar"는 IIMap::iterator의미 상 완전히 호환되지 않더라도 동일한 유형이기 때문에 유형 검사는 괜찮습니다 .

문제는 반복기 유형이 컨테이너 유형 에만 의존하는 것이 아니라 실제로 컨테이너 객체 에 의존해야 한다는 것입니다. 즉, "비 정적 멤버 유형"이어야합니다.

foo.iterator i = foo.begin();
bar.erase(i);  // ERROR: bar.iterator argument expected

이러한 기능, 즉 용어 (foo)에 의존하는 유형 (foo.iterator)을 표현하는 능력은 정확히 종속 입력이 의미하는 바입니다.

이 기능을 자주 보지 못하는 이유는 많은 수의 웜이 열리기 때문입니다. 컴파일 타임에 두 가지 유형이 동일한 지 확인하기 위해 갑자기 두 가지 표현을 증명해야하는 상황에 처하게됩니다. 동일합니다 (런타임에 항상 동일한 값을 생성 함). 당신은 위키 피 디아의 비교한다면 결과, 의존적으로 입력 된 언어의 목록을 그와 함께 정리 피 인증 목록 당신은 의심스러운 유사성을 알 수 있습니다. ;-)


4

책 유형 및 프로그래밍 언어 (30.5) 인용 :

이 책의 대부분은 다양한 종류의 추상화 메커니즘을 공식화하는 것과 관련이 있습니다. 간단하게 입력 된 람다-미적분에서 우리는 용어를 취하고 하위 용어를 추상화하는 작업을 공식화하여 나중에 다른 용어에 적용하여 인스턴스화 할 수있는 함수를 생성했습니다. System에서는 F용어를 취하고 형식을 추상화하여 다양한 형식에 적용하여 인스턴스화 할 수있는 용어를 생성하는 작업을 고려했습니다. 에λω, 우리는 단순히 형식화 된 람다-미적분 "한 단계 위로"의 메커니즘을 요약하여 형식을 취하고 하위 식을 추상화하여 나중에 다른 형식에 적용하여 인스턴스화 할 수있는 형식 연산자를 얻습니다. 이러한 모든 형태의 추상화를 편리하게 생각할 수있는 방법은 다른 표현에 의해 색인 된 표현 군에 관한 것입니다. 일반적인 람다 추상화 λx:T1.t2는 용어로 [x -> s]t1인덱싱 된 용어 군 s입니다. 마찬가지로 유형 추상화 λX::K1.t2는 유형별로 인덱싱 된 용어 모음이고 유형 연산자는 유형별로 인덱싱 된 유형 모음입니다.

  • λx:T1.t2 용어로 색인 된 용어 군

  • λX::K1.t2 유형별로 색인 된 용어 군

  • λX::K1.T2 유형별로 인덱싱 된 유형 계열

이 목록을 살펴보면 아직 고려하지 않은 한 가지 가능성이 있음이 분명합니다. 용어로 색인 된 유형의 패밀리입니다. 이러한 형태의 추상화는 종속 유형의 루 브릭에 따라 광범위하게 연구되었습니다.

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