저는 프로그래밍 언어 이론을 처음 사용합니다. 나는 강사가 다형성 유형의 함수가 forall t: Type, t->t
정체성 이라고 주장하는 온라인 강의를보고 있었지만 그 이유를 설명하지 못했습니다. 누군가 왜 나에게 설명 할 수 있습니까? 아마도 첫 번째 원칙의 주장에 대한 증거 일 수 있습니다.
저는 프로그래밍 언어 이론을 처음 사용합니다. 나는 강사가 다형성 유형의 함수가 forall t: Type, t->t
정체성 이라고 주장하는 온라인 강의를보고 있었지만 그 이유를 설명하지 못했습니다. 누군가 왜 나에게 설명 할 수 있습니까? 아마도 첫 번째 원칙의 주장에 대한 증거 일 수 있습니다.
답변:
가장 먼저 알아 두어야 할 것은 이것이 반드시 사실이 아니라는 것입니다. 예를 들어, 언어에 따라 해당 유형의 함수는 항등 함수일뿐 아니라 1) 영구 루프, 2) 일부 상태 변경, 3) return null
, 4) 예외 발생, 5) I / O 수행, 6) 스레드를 포크하여 다른 작업을 수행하십시오 .7) call/cc
shenanigans를 수행하십시오 .8) Java와 같은 것을 Object.hashCode
사용하십시오 .9) 리플렉션을 사용하여 유형이 정수인지 판별하고 증가하면 증가하십시오 .10) 리플렉션을 사용하여 호출 스택을 분석하고 11) 아마도 많은 다른 것들과 분명히 위의 임의의 조합으로 불리우는 맥락에서 무언가를하십시오.
파라 메트릭 성 (parametricity)을 이끄는 속성은 전체적으로 언어의 속성이며 그에 따라 더 강하고 약한 변형이 있습니다. 형식 이론에서 연구 된 많은 공식 미적분학에서 위의 행동 중 어느 것도 일어날 수 없습니다. 예를 들어, 파라 메트릭 성이 처음 연구 된 시스템 F / 순수 다형성 람다 미적분학의 경우 위의 동작 중 어느 것도 발생할 수 없습니다. 그것은 단순히 예외, 변경 가능한 상태가없는 null
, call/cc
, I / O, 반사, 그리고 강하게 영원히 루프 수 없도록 정상화입니다. Gilles가 의견에서 언급했듯이, Theorems 논문 은 무료입니다!필와 들러 (Phil Wadler)는이 주제에 대한 좋은 소개이며, 그 참고 문헌은 이론, 특히 논리 관계 기술에 대해 자세히 설명 할 것이다. 이 링크에는 매개 변수 주제에 관한 Wadler의 다른 논문도 나와 있습니다.
파라 메트릭은 언어의 속성이기 때문에 언어를 공식적으로 발음 한 다음 비교적 복잡한 주장을 요구합니다. 우리가 다형성 람다 미적분학에 있다고 가정 할 때이 특별한 경우에 대한 비공식적 인 주장은 우리가 아무것도 알지 t
못하기 때문에 입력에 대해 어떤 작업도 수행 할 수 없다는 것입니다 (예 : 입력인지 알 수 없으므로 증가시킬 수 없음) 숫자) 또는 해당 유형의 값을 작성하십시오 (우리가 아는 모든 t
= Void
값이없는 유형). 유형의 가치를 생산하는 유일한 방법 t
은 우리에게 주어진 것을 반환하는 것입니다. 다른 행동은 불가능합니다. 이를 확인하는 한 가지 방법은 강력한 정규화를 사용하고이 유형의 정규형 항이 하나만 있음을 보여주는 것입니다.
주장의 증거는 매우 복잡하지만 그것이 실제로 원하는 경우 Reynolds 의 주제에 관한 원본 논문 을 확인할 수 있습니다 .
핵심 아이디어는 매개 변수 다형성 함수를 보유한다는 것입니다 . 여기서 다형성 함수의 본문은 함수의 모든 단일 형태 인스턴스화에 대해 동일합니다. 이러한 시스템에서는 다형성 유형의 매개 변수 유형에 대한 가정을 할 수 없으며 범위의 유일한 값에 일반 유형이있는 경우 해당 유형과 관련이 없지만 반환하거나 다른 함수에 전달하지 않습니다. ve 정의, 그것은 결국 아무것도 반환하거나 전달할 수 있습니다 .... 등. 결국, 매개 변수를 반환하기 전에 ID 체인 기능 만 수행하면됩니다.
데릭이 언급 한 모든 경고와 설정 이론을 사용하여 발생하는 역설을 무시하면서 레이놀즈 /와 들러의 정신에 대한 증거를 스케치 해 보겠다.
유형의 기능 :
f :: forall t . t -> t
함수 가족 형 의해 인덱스 t .
다형성 함수를 공식적으로 정의하려면 유형을 값 세트로 취급하지 말고 관계로 취급해야합니다. Int
평등 관계를 유도하는 것과 같은 기본 유형 -예를 들어 두 Int
값이 같으면 관련됩니다. 관련 값을 관련 값에 매핑하면 함수가 관련됩니다. 흥미로운 사례는 다형성 함수입니다. 관련 유형을 관련 값에 맵핑합니다.
우리의 경우, 우리 는 유형의 두 가지 다형성 함수 와 g 사이의 관계를 설정하려고 합니다.
forall t . t -> t
유형 가 유형 t 와 관련이 있다고 가정하십시오 . 첫 번째 함수 F는 타입 매핑 들 값 - 여기에, 값 자체가 함수 인 F 의 유형 의 → 들 . 두 번째 함수는 유형 t 를 유형 t → t 의 다른 값 g t 에 매핑합니다 . 우리는 말할 F는 관련이 g 값 경우 F S 및 g t가 관련되어있다. 이러한 값 자체는 함수이므로 관련 값을 관련 값에 매핑하면 관련됩니다.
중요한 단계는 레이놀즈의 파라 메트릭 정리를 사용하는 것인데, 이는 모든 용어가 그 자체와 관련이 있다고 말합니다. 우리의 경우, 기능 f
은 그 자체와 관련이 있습니다. 즉,이 경우 s
에 관련된 t
, 또한 관련된 F t .
이제 두 유형 사이의 관계를 선택하고이 정리를 적용 할 수 있습니다. 첫 번째 유형을 단위 유형으로 선택합니다.이 유형은 ()
값이 하나뿐입니다 ()
. 두 번째 유형은 t
임의이지만 비어 있지 않습니다. 사이의이 관계를 직접 선택 ()
하고 t
단순히 한 쌍의 수 ((), c)
, c
유형의 몇 가지 값 t
(관계가 세트의 직교 제품의 하위 집합입니다). 모수 정리는 우리에게 는 f t 와 관련이 있어야합니다. 관련 값을 관련 값에 맵핑해야합니다. 첫 번째 함수 f ( 에는 선택의 여지가 없으므로 유일한 값을 (으)로다시매핑해야합니다. 따라서, 두 번째 함수 F t는 매핑해야에(관련된 유일한 값). 완전히 임의적이므로 f t 는i d t 이고,완전히 임의적이므로is입니다.()
()
c
c
()
c
t
f
id
내 블로그 에서 자세한 내용을 찾을 수 있습니다 .
편집 : 위의 의견이 누락 된 부분을 제공했습니다. 일부 사람들은 고의적으로 덜 완성 된 언어를 가지고 놀고 있습니다. 나는 그런 언어들에 대해서는 신경 쓰지 않는다. 실제로 사용하기에 완벽하지 않은 언어는 디자인하기가 어렵습니다. 이것의 나머지 부분은 이러한 이론을 전체 언어에 적용하려고 시도하는 상황에서 확장됩니다.
그릇된!
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)
g
f
정리를 유지하기 위해서는 우스꽝스러운 능력을
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]¹
훨씬 쉬운 타입 추론을 처리 할 수 있다고 가정 할 수 있습니다.
정리가 끝날 때까지 도메인을 제한하려고하면 결국에는 너무 멀리 제한해야합니다.
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]
컴파일해서는 안됩니다. 근본적인 문제는 런타임 배열 인덱싱이 더 이상 작동하지 않는다는 것입니다.
foil
어쨌든 수량 자는 무엇입니까 ?) 이것은 전혀 도움이되지 않습니다.