다형성 타입`forall t : Type, t-> t`를 가진 함수가 왜 항등 함수 여야합니까?


18

저는 프로그래밍 언어 이론을 처음 사용합니다. 나는 강사가 다형성 유형의 함수가 forall t: Type, t->t정체성 이라고 주장하는 온라인 강의를보고 있었지만 그 이유를 설명하지 못했습니다. 누군가 왜 나에게 설명 할 수 있습니까? 아마도 첫 번째 원칙의 주장에 대한 증거 일 수 있습니다.


3
이 질문은 중복되어야한다고 생각했지만 찾을 수 없습니다. cs.stackexchange.com/questions/341/… 은 일종의 후속 조치입니다. 표준 참조는 무료정리입니다! 필와 들러
Gilles 'SO- 악마 그만해

1
다른 작업을 수행하는이 유형의 일반 함수를 구성하십시오. 아무것도 없다는 것을 알게 될 것입니다.
Bergi

@ Bergi 예 카운터 예제를 찾을 수 없지만 여전히 그것을 증명하는 방법을 모르겠습니다.
abhishek

그러나 당신이 하나를 찾으려고 할 때 관찰 한 것은 무엇입니까? 당신이 시도하지 않은 이유는 무엇입니까?
Bergi

@Gilles 아마도 cs.stackexchange.com/q/19430/14663를 기억 하십니까 ?
Bergi

답변:


32

가장 먼저 알아 두어야 할 것은 이것이 반드시 사실이 아니라는 것입니다. 예를 들어, 언어에 따라 해당 유형의 함수는 항등 함수일뿐 아니라 1) 영구 루프, 2) 일부 상태 변경, 3) return null, 4) 예외 발생, 5) I / O 수행, 6) 스레드를 포크하여 다른 작업을 수행하십시오 .7) call/ccshenanigans를 수행하십시오 .8) Java와 같은 것을 Object.hashCode사용하십시오 .9) 리플렉션을 사용하여 유형이 정수인지 판별하고 증가하면 증가하십시오 .10) 리플렉션을 사용하여 호출 스택을 분석하고 11) 아마도 많은 다른 것들과 분명히 위의 임의의 조합으로 불리우는 맥락에서 무언가를하십시오.

파라 메트릭 성 (parametricity)을 이끄는 속성은 전체적으로 언어의 속성이며 그에 따라 더 강하고 약한 변형이 있습니다. 형식 이론에서 연구 된 많은 공식 미적분학에서 위의 행동 중 어느 것도 일어날 수 없습니다. 예를 들어, 파라 메트릭 성이 처음 연구 된 시스템 F / 순수 다형성 람다 미적분학의 경우 위의 동작 중 어느 것도 발생할 수 없습니다. 그것은 단순히 예외, 변경 가능한 상태가없는 null, call/cc, I / O, 반사, 그리고 강하게 영원히 루프 수 없도록 정상화입니다. Gilles가 의견에서 언급했듯이, Theorems 논문 은 무료입니다!필와 들러 (Phil Wadler)는이 주제에 대한 좋은 소개이며, 그 참고 문헌은 이론, 특히 논리 관계 기술에 대해 자세히 설명 할 것이다. 이 링크에는 매개 변수 주제에 관한 Wadler의 다른 논문도 나와 있습니다.

파라 메트릭은 언어의 속성이기 때문에 언어를 공식적으로 발음 한 다음 비교적 복잡한 주장을 요구합니다. 우리가 다형성 람다 미적분학에 있다고 가정 할 때이 특별한 경우에 대한 비공식적 인 주장은 우리가 아무것도 알지 t못하기 때문에 입력에 대해 어떤 작업도 수행 할 수 없다는 것입니다 (예 : 입력인지 알 수 없으므로 증가시킬 수 없음) 숫자) 또는 해당 유형의 값을 작성하십시오 (우리가 아는 모든 t= Void값이없는 유형). 유형의 가치를 생산하는 유일한 방법 t은 우리에게 주어진 것을 반환하는 것입니다. 다른 행동은 불가능합니다. 이를 확인하는 한 가지 방법은 강력한 정규화를 사용하고이 유형의 정규형 항이 하나만 있음을 보여주는 것입니다.


1
시스템 F는 타입 시스템이 감지 할 수없는 무한 루프를 어떻게 피했습니까? 일반적인 경우에는 해결할 수없는 것으로 분류됩니다.
Joshua 11

2
@Joshua-정지 문제에 대한 표준 불가능 증거는 처음에 무한 루프가 있다는 가정에서 시작됩니다. 따라서 시스템 F에 무한 루프가없는 이유에 대해 의문을 제기하는 것은 순환 추론입니다. 더 광범위하게, 시스템 F는 거의 튜링이 완료 되지 않았 으므로 그 증거의 가정을 만족시키지 않을 것입니다. 컴퓨터가 모든 프로그램이 종료한다는 것을 증명하기에 충분히 약합니다 (재귀 없음, while 루프 없음, 매우 약한 루프 등).
Jonathan Cast

@Joshua : 일반적인 경우에는 해결할 수 없으므로 많은 특별한 경우에 해결할 수 없습니다. 특히, 체계적으로 체계화 된 F 용어가되는 모든 프로그램이 중지되는 것으로 입증되었습니다. 이러한 모든 프로그램에 대해 일관된 증거가 있습니다. 분명히 이것은 시스템 F로 입력 할 수없는 다른 프로그램 이 있다는 것을 의미합니다 .
cody

15

주장의 증거는 매우 복잡하지만 그것이 실제로 원하는 경우 Reynolds 의 주제에 관한 원본 논문 을 확인할 수 있습니다 .

핵심 아이디어는 매개 변수 다형성 함수를 보유한다는 것입니다 . 여기서 다형성 함수의 본문은 함수의 모든 단일 형태 인스턴스화에 대해 동일합니다. 이러한 시스템에서는 다형성 유형의 매개 변수 유형에 대한 가정을 할 수 없으며 범위의 유일한 값에 일반 유형이있는 경우 해당 유형과 관련이 없지만 반환하거나 다른 함수에 전달하지 않습니다. ve 정의, 그것은 결국 아무것도 반환하거나 전달할 수 있습니다 .... 등. 결국, 매개 변수를 반환하기 전에 ID 체인 기능 만 수행하면됩니다.


8

데릭이 언급 한 모든 경고와 설정 이론을 사용하여 발생하는 역설을 무시하면서 레이놀즈 /와 들러의 정신에 대한 증거를 스케치 해 보겠다.

유형의 기능 :

f :: forall t . t -> t

함수 가족 형 의해 인덱스 t .에프

다형성 함수를 공식적으로 정의하려면 유형을 값 세트로 취급하지 말고 관계로 취급해야합니다. Int평등 관계를 유도하는 것과 같은 기본 유형 -예를 들어 두 Int값이 같으면 관련됩니다. 관련 값을 관련 값에 매핑하면 함수가 관련됩니다. 흥미로운 사례는 다형성 함수입니다. 관련 유형을 관련 값에 맵핑합니다.

우리의 경우, 우리 는 유형의 두 가지 다형성 함수 g 사이의 관계를 설정하려고 합니다.에프

forall t . t -> t

유형 가 유형 t 와 관련이 있다고 가정하십시오 . 첫 번째 함수 F는 타입 매핑 값 - 여기에, 값 자체가 함수 인 F 유형 . 두 번째 함수는 유형 t 를 유형 t t 의 다른 값 g t 에 매핑합니다 . 우리는 말할 F는 관련이 g 값 경우 F Sg t가 관련되어있다. 이러한 값 자체는 함수이므로 관련 값을 관련 값에 매핑하면 관련됩니다.에스에프에스에프에스에스에스에프에프에스

중요한 단계는 레이놀즈의 파라 메트릭 정리를 사용하는 것인데, 이는 모든 용어가 그 자체와 관련이 있다고 말합니다. 우리의 경우, 기능 f은 그 자체와 관련이 있습니다. 즉,이 경우 s에 관련된 t, 또한 관련된 F t .에프에스에프

이제 두 유형 사이의 관계를 선택하고이 정리를 적용 할 수 있습니다. 첫 번째 유형을 단위 유형으로 선택합니다.이 유형은 ()값이 하나뿐입니다 (). 두 번째 유형은 t임의이지만 비어 있지 않습니다. 사이의이 관계를 직접 선택 ()하고 t단순히 한 쌍의 수 ((), c), c유형의 몇 가지 값 t(관계가 세트의 직교 제품의 하위 집합입니다). 모수 정리는 우리에게 f t 와 관련이 있어야합니다. 관련 값을 관련 값에 맵핑해야합니다. 첫 번째 함수 f (에프()에프 에는 선택의 여지가 없으므로 유일한 값을 (으)로다시매핑해야합니다. 따라서, 두 번째 함수 F t는 매핑해야에(관련된 유일한 값). 완전히 임의적이므로 f ti d t 이고,완전히 임의적이므로is입니다.에프()()()에프cc()c에프나는tfid

내 블로그 에서 자세한 내용을 찾을 수 있습니다 .


-2

편집 : 위의 의견이 누락 된 부분을 제공했습니다. 일부 사람들은 고의적으로 덜 완성 된 언어를 가지고 놀고 있습니다. 나는 그런 언어들에 대해서는 신경 쓰지 않는다. 실제로 사용하기에 완벽하지 않은 언어는 디자인하기가 어렵습니다. 이것의 나머지 부분은 이러한 이론을 전체 언어에 적용하려고 시도하는 상황에서 확장됩니다.

그릇된!

function f(a): forall t: Type, t->t
    function g(a): forall t: Type, t->t
       return (a is g) ? f : a
    return a is f ? g : a

여기서 is연산자는 참조 식별을 위해 두 변수를 비교합니다. 즉, 동일한 값을 포함합니다. 동등한 가치가 아니라 같은 가치. 함수 f이며 g일부 정의와 동일하지만 동일하지 않습니다.

이 함수 자체가 전달되면 다른 것을 반환합니다. 그렇지 않으면 입력을 반환합니다. 다른 것 자체는 동일한 유형을 가지므로 대체 할 수 있습니다. 다시 말해, 리턴 은이지만 ID는을 리턴 f하기 때문에 ID는 아닙니다 .f(f)gf

정리를 유지하기 위해서는 우스꽝스러운 능력을

function cantor(n, <z, a>) : forall t: t: Type int, <int, t> -> <int, t>
    return n > 1 ? cantor((n % 2 > 0) ? (n + 1) : n / 2, <z + 1, a>) : <z, a>
return cantor(1000, <0, a>)[1]¹

훨씬 쉬운 타입 추론을 처리 할 수 ​​있다고 가정 할 수 있습니다.

정리가 끝날 때까지 도메인을 제한하려고하면 결국에는 너무 멀리 제한해야합니다.

  • 순수 기능 (변경 가능 상태, IO 없음). 좋아, 나는 그걸로 살 수있다. 많은 시간 동안 함수에 대한 증명을 실행하려고합니다.
  • 빈 표준 라이브러리. meh.
  • 없음 raise도하고 exit. 이제 우리는 구속되기 시작했습니다.
  • 하단 유형이 없습니다.
  • 이 언어에는 컴파일러가 종료해야한다고 가정하여 무한 재귀를 축소 할 수있는 규칙이 있습니다. 컴파일러는 사소한 무한 재귀를 거부 할 수 있습니다.
  • 어떤 식 으로든 증명할 수없는 것이 제시되면 컴파일러가 실패 할 수 있습니다 .² 이제 표준 라이브러리는 함수를 인수로 사용할 수 없습니다. 우우.
  • 없습니다 nil. 문제가 발생하기 시작했습니다. 우리는 1 / 0.³을 다루는 방법이 부족했습니다.
  • 언어는 분기 유형 유추를 수행 할 수 없으며 프로그래머가 언어가 할 수없는 유형 유추를 입증 할 수있는 경우 재정의 할 수 없습니다. 이것은 꽤 나쁘다.

마지막 두 제약 조건이 모두 존재하여 언어가 무너졌습니다. 여전히 튜링이 완성 된 상태에서 일반적인 목적의 작업을 수행 할 수있는 유일한 방법은 언어가 덜 필요한 언어를 해석하는 내부 플랫폼을 시뮬레이션하는 것입니다.

¹ 컴파일러가이를 추론 할 수 있다고 생각되면이 시도

function fermat(z) : int -> int
    function pow(x, p)
        return p = 0 ? 1 : x * pow(x, p - 1)
    function f2(x, y, z) : int, int, int -> <int, int>
        left = pow(x, 5) + pow(y, 5)
        right = pow(z, 5)
        return left = right
            ? <x, y>
            : pow(x, 5) < right
                ? f2(x + 1, y, z)
                : pow(y, 5) < right
                    ? f2(2, y + 1, z)
                    : f2(2, 2, z + 1)
    return f2(2, 2, z)
function cantor(n, <z, a>) : forall t: t: Type int, <int, t> -> <int, t>
    return n > 1 ? cantor((n % 2 > 0) ? (n + 1) : n / 2, <z + 1, a>) : <z, a>
return cantor(fermat(3)[0], <0, a>)[1]

² 컴파일러가이를 수행 할 수 없다는 증거는 실명에 달려 있습니다. 컴파일러가 루프를 한 번에 볼 수 없도록 여러 라이브러리를 사용할 수 있습니다. 또한 컴파일러가 사용 가능한 메모리에서 유도를 수행 할 수 없기 때문에 프로그램이 작동하는 위치를 항상 작성할 수는 있지만 컴파일 할 수 없습니다.

³ 누군가 임의의 제네릭 형식이 nil을 반환하지 않고이 nil을 반환 할 수 있다고 생각합니다. 이것은 내가 지불 할 수있는 효과적인 언어를 보지 못한 불쾌한 페널티를 지불합니다.

function f(a, b, c): t: Type: t[],int,int->t
    return a[b/c]

컴파일해서는 안됩니다. 근본적인 문제는 런타임 배열 인덱싱이 더 이상 작동하지 않는다는 것입니다.


@ Bergi : 나는 반례를 만들었습니다.
Joshua

1
답변과 다른 두 가지의 차이점을 잠시 생각해보십시오. 데릭의 첫 문장은“먼저 주목할 점은 이것이 사실이 아니라는 것”입니다. 그리고 그는 언어의 어떤 속성이 그것을 실현시키는 지 설명 합니다. jmite는 또한 그것이 무엇이되는지 설명 합니다. 반대로, 당신의 대답은 설명이없는 지정되지 않은 (그리고 일반적이지 않은 언어)의 예를 보여줍니다. ( foil어쨌든 수량 자는 무엇입니까 ?) 이것은 전혀 도움이되지 않습니다.
Gilles 'SO- 악마 그만해

1
@DW : a가 f이면 a의 유형은 f의 유형이며, 이는 g의 유형이기도하므로 유형 검사를 통과해야합니다. 실제 컴파일러가 그것을 쫓아 내면 나는 실제 언어가 정적 유형 시스템에 대해 항상 잘못하고 런타임에 실패하지 않을 런타임 캐스트를 사용합니다.
Joshua

2
정적 유형 검사기가 작동하는 방식이 아닙니다. 유형이 단일 특정 입력과 일치하는지 확인하지 않습니다. 함수가 가능한 모든 입력에 대해 형식 검사를 수행 할 수 있도록 특정 형식 규칙이 있습니다. 타입 캐스트를 사용해야하는 경우이 솔루션은 훨씬 덜 흥미 롭습니다. 물론 타입 시스템을 우회하면 함수의 타입은 아무것도 보장하지 않습니다.
DW

1
@DW : 요점을 놓쳤다. 정적 유형 검사기가 코드를 찾는 데 재치가 있다면 코드가 유형 안전하다는 것을 증명할 수있는 충분한 정보가 있습니다.
Joshua
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.