의존적으로 타이핑하지 않는 이유는 무엇입니까?


161

나는 여러 출처에서 "Haskell이 점차 의존형 언어가되고있다"라는 의견을 반향시켰다. 더 많은 언어 확장으로 Haskell은 일반적인 방향으로 표류하고 있지만 아직은 그렇지 않습니다.

기본적으로 알고 싶은 두 가지가 있습니다. 첫 번째는, "종종 의존형 언어"라는 것이 실제로 무엇을 의미 하는가? (기술적으로 너무 기술적 인 일이 없기를 바랍니다.)

두 번째 질문은 ... 단점은 무엇입니까? 사람들은 우리가 그런 식으로 가고 있다는 것을 알고 있기 때문에 이점이 있어야합니다. 그러나 우리는 아직 거기에 없기 때문에 사람들을 완전히 방해하는 몇 가지 단점이 있습니다. 문제가 복잡성이 급격히 증가하고 있다는 인상을받습니다. 그러나 의존적 인 타이핑이 무엇인지 실제로 이해하지 못하면 확실하지 않습니다.

내가 알고 것은 내가 의존적 형식의 프로그래밍 언어에 대한 책을 읽은 시작할 때마다이 텍스트가 문제의 그 아마도 ... 완전히 이해할 수없는 것입니다. (?)


10
간단히 말해서 용어 (계산)에 의존하는 유형을 작성할 수 있습니다. 이는 프로그램의 모든 측면에 대한 유형을 지정하기에 충분하므로 유형 시스템이 전체 프로그램 스펙을 수행 할 수 있음을 의미합니다. 문제는 유형이 계산에 의존하기 때문에 유형 검사가 수행하기가 훨씬 어렵다는 것입니다 (일반적으로 불가능 함).
GManNickG

27
@GManNickG : 타입 검사는 전적으로 가능합니다. 타입 추론 은 또 다른 문제이지만, GHC의 다양한 확장은 오랫동안 모든 타입을 추론 할 수 있어야한다는 생각을 버렸다.
CA McCann

7
내가 올바르게 이해한다면 단점은 의존적 인 타이핑을 올바르게 수행하는 것이 어렵다는 것입니다 (예 : 사용 가능하고 잘 설립 된 방식으로)는 어렵습니다 .
오는 폭풍

1
@CAMcCann : 예, 제 실수입니다.
GManNickG

4
나는 누군가가 하나의 큰 실용적인 단점을 지적했다고 생각하지 않습니다. 모든 코드가 정확하다는 증거를 작성하는 것은 꽤 지루합니다. 타입 추론을 자동으로 수행 할 수 없기 때문에 ( "강력한"논리로 증명되는 정리에 해당), 증명 형식으로 프로그램의 주석을 작성해야합니다. 이것은 특히 사람들이 Haskell에서 일반적으로하는 더 정교한 수도원 마법을 위해 성가신 일을하기 어렵게 만듭니다. 요즘 가장 많이오고있는 언어는 대부분 우리를 위해이 언어를 사용하거나 훌륭한 기본 요소를 제공하는 언어입니다.
Kristopher Micinski

답변:


21

의존적 타이핑은 실제로는 값과 유형 수준의 통일이므로 유형에 대한 값을 매개 변수화 할 수 있으며 (Haskell의 유형 클래스 및 매개 변수 다형성으로 이미 가능) 값에 대한 유형을 매개 변수화 할 수 있습니다 (엄격히 말하지는 않지만 Haskell에서 가능) DataKinds아주 가까이 있지만 ).

편집 : 분명히,이 시점부터, 나는 틀렸다 (@pigworker의 의견 참조). 나는 내가 먹인 신화의 기록으로 이것의 나머지를 보존 할 것이다. :피


내가 들었던 것에서 완전 의존형 타이핑으로 옮기는 문제는 타입과 값 레벨 사이의 단계 제한을 깨뜨려 하스켈이 지워진 유형의 효율적인 기계 코드로 컴파일 될 수 있다는 것입니다. 현재 수준의 기술을 사용하면 종속 형식화 된 언어 특정 시점 (즉, 종속 형식화 된 바이트 코드 또는 이와 유사한 형식으로 컴파일 된 후)에서 인터프리터를 거쳐야 합니다 .

이것은 반드시 근본적인 제한은 아니지만, 이와 관련하여 유망 해 보이지만 아직 GHC로 만들지 않은 현재 연구에 대해서는 개인적으로 알고 있지 않습니다. 다른 사람이 더 많은 것을 알고 있다면 기꺼이 정정 할 것입니다.


46
당신이 말하는 것은 거의 전적으로 거짓입니다. 전적으로 당신을 비난하지는 않습니다. 그것은 표준 신화를 사실로 반복합니다. Edwin Brady의 언어 인 Idris는 유형 삭제 (런타임 동작이 유형에 의존하지 않기 때문에)를 수행하고 스톡 G- 머신 기술을 사용하여 코드를 생성하는 상당히 표준적인 람다-리프트 수퍼 콤비 네이터 인코딩을 생성합니다.
pigworker

3
참고로 누군가 최근 에이 논문을 지적했다 . 내가 알 수 있듯이 Haskell은 의존적으로 친절하게 만들 것입니다 (즉, 유형 수준 언어는 의존적으로 유형화 될 것입니다).
Ptharien 's Flame

8
그렇습니다.이 논문은 타입을 물건에 의존하는 방법과 타입 / 종류의 구별을 없애는 방법을 보여주기 위해 대부분의 방법을 사용합니다. 이미 논의중인 타당한 후속 조치는 실제 종속 함수 유형을 허용하지만 값과 유형 계층 (현재 데이터 유형 승격 덕분에 중요하지 않음)에 존재할 수있는 언어 조각으로 인수를 제한하는 것입니다. 이는 현재 "가짜"를 원하는 것보다 더 복잡하게 만드는 싱글 톤 구조의 필요성을 제거 할 것입니다. 우리는 실제와 점점 더 가까워지고 있습니다.
pigworker

13
하스켈에 종속적 인 유형을 개조하는 실용적인 질문이 많이 있습니다. 이 제한된 형태의 종속 함수 공간을 얻은 후에도 유형 수준에서 허용되는 값 언어의 조각을 확대하는 방법과 방정식 이론이 무엇인지 (2 + 2를 원할 때) 4 등). 스크래치에 따라 독립적으로 유형이 지정된 언어가 디자인과 관련이없는 까다로운 문제 (예 : 하단)가 많이 있습니다.
pigworker

2
@pigworker Haskell의 전체 하위 집합이 있습니까? 그렇다면 "값과 유형 계층 모두에 존재할 수있는 언어의 조각"에 대해 이것을 사용할 수 없었습니까? 그렇지 않다면 하나를 생산하는 데 무엇이 필요할까요?
Ptharien 's Flame

223

의존적으로 입력 된 Haskell, 지금?

하스켈은 어느 정도 의존적으로 형식화 된 언어입니다. 타입 레벨 데이터에 대한 개념이 있는데, 이제 덕분에 더 잘 타이핑되었습니다 DataKinds. 그리고 GADTs타입 레벨 데이터에 런타임 표현을 제공하는 몇 가지 수단 ( )이 있습니다. 따라서 런타임 항목의 값이 types에 효과적으로 표시 되므로 언어를 종속적으로 입력해야합니다.

단순 데이터 유형은 종류 레벨 로 승격 되어 포함 된 값을 유형에 사용할 수 있습니다. 그러므로 전형적인 예

data Nat = Z | S Nat

data Vec :: Nat -> * -> * where
  VNil   :: Vec Z x
  VCons  :: x -> Vec n x -> Vec (S n) x

가능해지면서

vApply :: Vec n (s -> t) -> Vec n s -> Vec n t
vApply VNil         VNil         = VNil
vApply (VCons f fs) (VCons s ss) = VCons (f s) (vApply fs ss)

좋네요 길이 n는 해당 함수에서 정적으로 정적이므로 입력 및 출력 벡터의 길이는 동일하지만 실행 시간에는 아무런 영향을 미치지 않습니다 vApply. 반면에, 그것은 만드는 기능을 구현하는 (즉, 불가능) 훨씬 복잡합니다 n주어진의 사본 x(있을 것이다 purevApply의 '를 <*>)

vReplicate :: x -> Vec n x

런타임에 사본 수를 아는 것이 중요하기 때문입니다. 싱글 톤을 입력하십시오.

data Natty :: Nat -> * where
  Zy :: Natty Z
  Sy :: Natty n -> Natty (S n)

모든 승격 가능한 유형의 경우, 값의 런타임 복제본에 의해 승격 된 승격 된 유형에 대해 인덱스 된 싱글 톤 패밀리를 구축 할 수 있습니다. Natty ntype-level의 런타임 사본 유형 n :: Nat입니다. 우리는 지금 쓸 수 있습니다

vReplicate :: Natty n -> x -> Vec n x
vReplicate Zy     x = VNil
vReplicate (Sy n) x = VCons x (vReplicate n x)

따라서 런타임 레벨로 요크되는 유형 레벨 값이 있습니다. 런타임 사본을 검사하면 유형 레벨 값에 대한 정적 지식이 세분화됩니다. 용어와 유형이 분리되어 있지만 단일 톤 구조를 에폭시 수지의 종류로 사용하여 상 사이에 결합을 만들어 의존적으로 유형을 지정할 수 있습니다. 형식에 임의의 런타임 식을 허용하는 데는 먼 길이지만 아무것도 아닙니다.

불쾌한 무엇입니까? 무엇이 빠졌습니까?

이 기술에 약간의 압력을 가하고 흔들기 시작하는 것을 봅시다. 싱글 톤을 좀 더 암시 적으로 관리 할 수 ​​있어야한다는 생각이들 수도 있습니다.

class Nattily (n :: Nat) where
  natty :: Natty n
instance Nattily Z where
  natty = Zy
instance Nattily n => Nattily (S n) where
  natty = Sy natty

우리가 글을 쓸 수 있도록

instance Nattily n => Applicative (Vec n) where
  pure = vReplicate natty
  (<*>) = vApply

그것은 효과가 있지만 이제는 우리의 원래 Nat유형이 종류, 싱글 톤 패밀리 및 싱글 톤 클래스의 세 사본을 생성 했음을 의미합니다 . 우리는 명백한 Natty n가치와 Nattily n사전 을 교환하기 위해 다소 성가신 과정을 가지고 있습니다 . 더구나, Natty아닙니다 Nat: 우리는 런타임 값에 어떤 종류의 의존성이 있지만 우리가 처음 생각했던 유형에는 없습니다. 완전 의존형 언어는 의존형을 복잡하게 만듭니다!

한편, Nat승진시킬 수는 있지만 할 수 Vec는 없습니다. 인덱싱 된 유형으로 인덱싱 할 수 없습니다. 의존적으로 유형이 지정된 언어로 가득 찬 그런 제한은 없으며, 내 경력에서 의존적으로 유형이 지정된 과시로, 나는 1 층 색인을 만든 사람들을 가르치기 위해 대화에 2 층 색인의 예를 포함시키는 법을 배웠습니다. 카드 집처럼 접을 것을 기대할 수는 없습니다. 뭐가 문제 야? 평등. GADT는 생성자에게 특정 반환 유형을 명시적인 방정식 요구로 제공 할 때 달성 한 제약 조건을 암시 적으로 변환하여 작동합니다. 이렇게

data Vec (n :: Nat) (x :: *)
  = n ~ Z => VNil
  | forall m. n ~ S m => VCons x (Vec m x)

두 방정식 각각에서 양변은 종류가 Nat있습니다.

이제 벡터를 통해 색인을 생성 한 것과 동일한 변환을 시도하십시오.

data InVec :: x -> Vec n x -> * where
  Here :: InVec z (VCons z zs)
  After :: InVec z ys -> InVec z (VCons y ys)

된다

data InVec (a :: x) (as :: Vec n x)
  = forall m z (zs :: Vec x m). (n ~ S m, as ~ VCons z zs) => Here
  | forall m y z (ys :: Vec x m). (n ~ S m, as ~ VCons y ys) => After (InVec z ys)

이제 우리는 양측이 구문 적으로 구별되는 (그러나 아마도 똑같은) 종류를 갖는 장소 사이 as :: Vec n x와 방정식의 제약 조건을 형성 VCons z zs :: Vec (S m) x합니다. GHC 코어는 현재 이러한 개념을 갖추고 있지 않습니다!

무엇이 더 빠졌습니까? 음, 하스켈의 대부분은 유형 수준에서 누락되었습니다. 승격 할 수있는 용어의 언어에는 실제로 변수와 비 GADT 생성자가 있습니다. 일단 당신이 그것들을 가지고 type family있다면 , 기계는 타입 레벨 프로그램을 작성할 수있게합니다 : 그것들 중 일부는 당신이 용어 레벨에서 쓰는 것을 고려하는 기능과 매우 유사 할 것입니다 (예를 들어, Nat추가를 장착 하여 추가하기에 좋은 타입을 줄 수 있습니다 Vec) 그러나 그것은 우연의 일치입니다!

실제로 누락 된 또 다른 것은 값으로 유형을 색인화하는 새로운 기능을 사용 하는 라이브러리 입니다. 이 용감한 새로운 세상에서 무엇을 Functor 하고 Monad있습니까? 나는 그것에 대해 생각하고 있지만 여전히 할 일이 많이 있습니다.

타입 레벨 프로그램 실행

Haskell은 가장 의존적으로 유형이 지정된 프로그래밍 언어와 같이 두 가지 작동 의미가 있습니다. 런타임 시스템이 프로그램을 실행하는 방법이 있습니다 (닫힌 표현식 만 유형 삭제 후 최적화 됨). 그러면 유형 검사기가 프로그램을 실행하는 방식이 있습니다 (유형 식, "유형 클래스 Prolog", 개방 표현식). Haskell의 경우, 실행되는 프로그램이 다른 언어로되어 있기 때문에 일반적으로 두 가지를 섞지 않습니다. 의존적으로 유형이 지정된 언어는 동일한 언어의 프로그램에 대해 별도의 런타임 및 정적 실행 모델을 갖지만 걱정할 필요는 없지만 런타임 모델을 사용하면 유형 삭제 및 실제로 증거 삭제를 수행 할 수 있습니다. 이것이 바로 Coq의 추출입니다.메커니즘은 당신을 제공합니다; Edwin Brady의 컴파일러는 최소한 Edwin Brady가하는 일입니다 (Edwin은 불필요하게 중복 된 값과 유형 및 증명을 지우지 만). 위상 구분은 더 이상 구문 범주 의 구별이 아닐 수 있지만 살아 있고 좋습니다.

의존적으로 타이핑 된 언어는 전체적으로, 타입 체커가 오래 기다리는 것보다 더 나쁜 것에 대한 두려움으로부터 프로그램을 실행할 수있게한다. Haskell의 의존도가 높아짐에 따라 정적 실행 모델이 무엇인지에 대한 질문에 직면하게됩니까? 하나의 접근법은 정적 실행을 총 함수로 제한하여 동일한 자유를 실행할 수는 있지만 데이터와 코 데이터를 구별 할 수 있도록 (최소한 유형 레벨 코드의 경우) 강제 할 수 있습니다. 종료 또는 생산성을 강화합니다. 그러나 이것이 유일한 접근법은 아닙니다. 우리는 계산을 통해 더 적은 수의 방정식을 만들어내는 비용으로 프로그램을 실행하는 것을 꺼리는 훨씬 약한 실행 모델을 자유롭게 선택할 수 있습니다. 그리고 실제로, 그것은 GHC가 실제로하는 일입니다. GHC 코어의 타이핑 규칙은 달리기를 언급하지 않습니다. 프로그램, 그러나 방정식에 대한 증거를 확인합니다. 코어로 변환 할 때 GHC의 제한 솔버는 타입 레벨 프로그램을 실행하려고 시도하여 주어진 표현식이 정규 형식과 동일한 증거를 약간 은빛으로 만듭니다. 이 증거 생성 방법은 예측할 수없고 불가피하게 불완전합니다. 예를 들어, 무서운 것처럼 보이는 재귀에 맞서 싸울 수 있습니다. 아마 현명 할 것입니다. 우리가 걱정할 필요가없는 것은 타입 IO 체커에서 계산을 실행하는 것 입니다. 타입 체커는 launchMissiles런타임 시스템과 동일한 의미 를 부여 할 필요는 없습니다 .

힌들리-밀너 문화

Hindley-Milner 타입 시스템은 많은 사람들이 구별의 차이를 볼 수없고 우연의 일치가 불가피하다고 가정하는 불행한 문화적 부작용으로 네 가지 구별의 진정한 우연의 일치를 달성합니다! 내가 무슨 소리 야?

  • 용어 유형
  • 명시 적으로 작성된 것 vs 암시 적으로 작성된 것
  • 런타임 전 존재 vs 런타임 전 삭제
  • 비 의존적 추상화 의존적 정량화

우리는 용어를 쓰고 유형을 유추 한 다음 지워 버리는 데 익숙합니다. 우리는 해당 유형 추상화 및 응용 프로그램이 자동 및 정적으로 발생하는 유형 변수를 수량화하는 데 익숙합니다.

이러한 구별이 맞지 않기 전에 바닐라 힌들리-밀 너와 너무 멀리 떨어져있을 필요는 없으며, 이는 나쁘지 않습니다 . 처음에는 몇 군데에 기꺼이 글을 쓰려고하면 더 흥미로운 유형을 가질 수 있습니다. 한편, 오버로드 된 함수를 사용할 때 유형 클래스 사전을 작성할 필요는 없지만 런타임에 해당 사전이 존재하거나 인라인되어 있습니다. 의존적으로 유형이 지정된 언어에서는 런타임에 단순한 유형 이상의 것을 지울 것으로 예상되지만 유형 클래스와 같이 암시 적으로 유추 된 값은 지워지지 않습니다. 예를 들어 vReplicate의 숫자 인수는 종종 원하는 벡터의 유형에서 추론 할 수 있지만 여전히 런타임에 알아야합니다.

이러한 우연의 일치가 더 이상 없기 때문에 어떤 언어 디자인 선택을 검토해야합니까? 예를 들어, Haskell이 forall x. t정량 자를 명시 적으로 인스턴스화하는 방법을 제공하지 않는 것이 맞 습니까? x타입 체커가 unifiying하여 추측 할 수 없다면 t, 우리는 무엇이 x되어야 하는지 말할 다른 방법이 없다 .

더 광범위하게, 우리는 "유형 추론"을 우리가 가지고 있거나 전혀 가지고 있지 않은 모 놀리 식 개념으로 취급 할 수 없습니다. 시작하려면 "일반화"측면 (Milner의 "let"규칙)을 분리해야합니다.이 규칙은 어리석은 기계가 "전문화"측면 (Milner ' "var "rule) 제약 조건 솔버만큼 효과적입니다. 최상위 유형은 추론하기가 더 어려워 질 것으로 예상되지만 내부 유형 정보는 쉽게 전파 할 수 있습니다.

하스켈의 다음 단계

우리는 유형과 종류 수준이 매우 비슷해 짐을보고 있습니다 (그리고 이미 GHC에서 내부 표현을 공유하고 있습니다). 우리는 그것들을 합칠 수도 있습니다. * :: *우리가 할 수 있다면 재미있을 것입니다 . 오래 전에 논리적으로 소리를 잃었을 때, 바닥을 허용했지만 유형 소리는 일반적으로 약한 요구 사항입니다. 확인해야합니다. 고유 한 유형, 종류 등의 수준이 있어야하는 경우 최소한 유형 수준 이상의 모든 항목을 항상 홍보 할 수 있습니다. 종류 수준에서 다형성을 다시 발명하는 대신 유형에 이미 존재하는 다형성을 재사용하는 것이 좋습니다.

우리는 단순화시킴으로써 제약 현재 시스템을 일반화한다 이종 방정식 a ~ b의 종류 ab구문이 일치하지 않는 (하지만, 동일한 검증을 할 수있다). 그것은 의존성을 훨씬 쉽게 다루는 오래된 기술입니다 (지난 세기의 논문에서). GADT의 표현식에 대한 제약을 표현할 수 있으므로 승격 할 수있는 것에 대한 제한을 완화 할 수 있습니다.

종속 함수 유형을 도입하여 싱글 톤 구성의 필요성을 제거해야합니다 pi x :: s -> t. 이러한 유형의 함수 는 유형과 용어 언어 (따라서 변수, 생성자 등) 의 교차점 에있는 유형의 모든 표현식에 명시 적 으로 적용될 수 있습니다 . 해당 람다 및 응용 프로그램은 런타임에 지워지지 않으므로 작성할 수 있습니다.s

vReplicate :: pi n :: Nat -> x -> Vec n x
vReplicate Z     x = VNil
vReplicate (S n) x = VCons x (vReplicate n x)

바꾸지 않고 Nat의해 Natty. 의 도메인은 pi모든 승격 가능한 유형이 될 수 있으므로 GADT를 승격 할 수있는 경우 종속 수량화 시퀀스 (또는 Britejn이라고하는 "망원경")를 작성할 수 있습니다.

pi n :: Nat -> pi xs :: Vec n x -> ...

원하는 길이로

이 단계의 핵심은 약한 도구와 복잡한 인코딩을 사용하는 대신보다 일반적인 도구로 직접 작업 하여 복잡성제거 하는 것입니다. 현재 부분 바이 인은 Haskell의 일종의 종속 유형의 이점을 필요한 것보다 더 비싸게 만듭니다.

너무 열심히?

의존성 유형은 많은 사람들을 긴장하게 만듭니다. 그들은 나를 긴장하게 만들지 만, 나는 긴장하는 것을 좋아하거나 적어도 어쨌든 긴장하지 않는 것을 느낍니다. 그러나 주제에 대해 무지한 안개가 있다는 것을 돕는 것은 아닙니다. 그 중 일부는 우리 모두가 여전히 배울 점이 많기 때문입니다. 그러나 덜 급진적 인 접근을지지하는 사람들은 사실이 전적으로 그들과 함께 있는지 확인하지 않고 의존적 유형에 대한 두려움을 떨쳐내는 것으로 알려져 있습니다. 나는 이름을 짓지 않을 것이다. 이러한 "불확실한 유형 검사", "불완전한 투어링", "단계 구분 없음", "타입 삭제 없음", "모든 곳에서 증거 없음"등 신화는 비록 쓰레기 일지라도 지속됩니다.

의존적으로 유형이 지정된 프로그램이 항상 올바른 것으로 입증되어야하는 것은 아닙니다. 프로그램의 기본 위생을 향상시켜 전체 사양을 따르지 않고도 유형의 추가 불변성을 적용 할 수 있습니다. 이 방향으로의 작은 단계는 종종 추가 증거 의무가 거의 없거나 전혀없이 훨씬 더 강력한 보증을 제공합니다. 의존적으로 유형이 지정된 프로그램이 필연적 으로 증명으로 가득 하다는 것은 사실이 아닙니다 . 실제로 필자는 일반적으로 내 정의의문제기 하는 신호로 내 코드에 증명을 제공 합니다 .

왜냐하면, 조음 증의 증가와 마찬가지로, 우리는 새로운 것뿐만 아니라 공정한 파울을 자유롭게 말할 수 있습니다. 예를 들어, 이진 검색 트리를 정의하는 많은 어리석은 방법이 있지만 이것이 좋은 방법 이 아니라는 것을 의미하지는 않습니다 . 자존심이 그것을 인정하도록 허약하더라도 나쁜 경험이 더 나아질 수 없다고 가정하지 않는 것이 중요합니다. 의존적 정의의 디자인은 학습을 필요로하는 새로운 기술이며, Haskell 프로그래머가 자동으로 전문가가되지는 않습니다! 그리고 어떤 프로그램이 파울지라도 다른 사람들이 공정한 자유를 거부하는 이유는 무엇입니까?

왜 아직도 Haskell을 귀찮게합니까?

나는 의존적 인 유형을 정말로 좋아하지만 대부분의 해킹 프로젝트는 여전히 Haskell에 있습니다. 왜? 하스켈에는 타입 클래스가 있습니다. 하스켈에는 유용한 라이브러리가 있습니다. Haskell은 효과가있는 프로그래밍을 처리 할 수 ​​있습니다 (이상적이지는 않지만). Haskell에는 산업 강도 컴파일러가 있습니다. 의존적으로 유형이 지정된 언어는 커뮤니티 및 인프라의 성장에있어 훨씬 초기 단계에 있지만 메타 프로그래밍 및 데이터 유형 제네릭 등을 통해 가능한 세대를 실질적으로 변화시켜 나갈 것입니다. 그러나 Haskell이 종속 유형을 향한 발걸음의 결과로 사람들이 무엇을하고 있는지 살펴보면 현재 언어의 발전을 통해 많은 이점을 얻을 수 있습니다.


6
아직 DataKinds에 대해서는 신경 쓰지 않습니다. 주로 다음과 같이하고 싶기 때문에 : fmap read getLine >>= \n -> vReplicate n 0. 아시다시피, 이것 Natty과 다른 방법입니다. 또한 vReplicate 실제 메모리 어레이, 같은 것으로 번역되어야 newtype SVector n x = SVector (Data.Vector.Vector x), n종류 갖는다 Nat(또는 유사). 아마도 "종속적으로 유형화 된 과시"에 대한 또 다른 시연 점은?
John L

7
효과를 이용한 프로그래밍의 이상적인 처리에 대해 염두에 두어야 할 것이 있습니까?
Steven Shaw

6
훌륭한 글을 보내 주셔서 감사합니다. 일부 데이터가 프로그램 외부에서 시작되는 (예 : 파일에서 읽은) 종속 설정 코드의 예를보고 싶습니다. 값을 유형으로 승격시키는 것이 그러한 설정에서 어떻게 보일지에 대한 느낌을 얻으려고합니다. 나는 모든 예제에 정적으로 알려진 크기의 벡터 (목록으로 구현 됨)가 포함되어 있다고 생각합니다.
tibbe

4
@pigworker 당신은 신화로 "위상 구분 없음"을 취합니다 (내가 동의하는 다른 것들은 신화입니다). 그러나 당신은 내가 본 논문과 대화에서 이것을 해체하지 않았으며, 내가 존중하는 다른 사람은 "종속 검사는 유형 검사, 컴파일 및 실행 단계를 의미있게 분리 할 수 ​​없기 때문에 의존적 유형 이론은 일반적인 컴파일러와 다릅니다. " 내 경험 "가짜"에서 때때로 위상을 지울 필요는 없지만 적어도 위상 구분을 흐리게합니다. 이 문제에 대해 다른 곳이 아니라면 확장 할 수 있습니까?
sclv

4
@sclv 저의 작업은 특별히 "위상 구별이없는"신화를 목표로하지 않았지만 다른 사람들도 그랬습니다. 제임스 맥킨 나 (James McKinna)와 에드윈 브래디 (Edwin Brady)의 "에피 그램 (Epigram) 편집의 위상 구분 (Phase Distinctions)"을 추천합니다. 그러나 Coq.의 프로그램 추출에 대한 훨씬 오래된 작업도 참조하십시오. 타입 체커에 의해 수행되는 공개 기간 평가는 추출을 통한 실행과 ML로 완전히 분리되어 있으며 추출이 타입과 증명을 제거한다는 것이 분명합니다.
pigworker

20

존은 종속 유형에 대한 또 다른 일반적인 오해입니다. 데이터가 런타임에만 사용 가능할 때 작동하지 않는다는 것입니다. getLine 예제를 수행하는 방법은 다음과 같습니다.

data Some :: (k -> *) -> * where
  Like :: p x -> Some p

fromInt :: Int -> Some Natty
fromInt 0 = Like Zy
fromInt n = case fromInt (n - 1) of
  Like n -> Like (Sy n)

withZeroes :: (forall n. Vec n Int -> IO a) -> IO a
withZeroes k = do
  Like n <- fmap (fromInt . read) getLine
  k (vReplicate n 0)

*Main> withZeroes print
5
VCons 0 (VCons 0 (VCons 0 (VCons 0 (VCons 0 VNil))))

편집 : 흠, 그것은 pigworker의 답변에 대한 의견으로 생각되었습니다. 나는 분명히 실패합니다.


첫 문장은 조금 이상해 보인다. 나는 의존 종류의 점들이 있다는 것입니다 말을 데이터가 런타임에서만 사용할 수있을 때 일을. 그러나이 CPS 스타일 기술은 동일하지 않습니다. 함수가 있다고 가정합니다 Vec Zy -> IO String. 당신은 그것을 사용할 수 없습니다 withZeroes유형이 있기 때문에, ZyFORALL N과 통합 할 수 없습니다. 어쩌면 한두 가지 특별한 경우에 해결할 수는 있지만 빨리 벗어날 수 있습니다.
John L

getLine의 String과 같은 간단한 유형의 값을 가져 와서 더 강한 유형 (위의 Natty n과 같은)으로 바꾸는 경우 핵심은 유형 검사기에 필요한 동적 검사를 수행하고 있음을 확신시켜야한다는 것입니다. 귀하의 예에서는 임의의 숫자를 읽고 있으므로 forall n의미가 있습니다. 보다 정확한 제한은 동일한 방식으로 구현 될 수 있습니다. 보다 좋은 예가 있습니까 Vec Zy(프로그램은 여전히 ​​0 대신 5를 입력하는 사용자를 처리해야합니다)?
ulfnorell

1
첫 번째 문장으로 말하려는 것은 외부 세계와 상호 작용하여 데이터를 얻는 경우 종속 유형을 사용할 수 없다고 생각하는 사람들에게 가끔 닥친다는 것입니다. 내 요점은 당신이해야 할 유일한 일은 일반적으로 간단한 종속 형 파서를 작성하는 것입니다.
ulfnorell

1
ulfnorell : 죄송합니다. 확실하지 않았습니다. 에 대해 작동하는 하나의 함수 Vec Zy -> IO String와에 대해 다른 함수가 Vec n -> IO String있고 유형이 일치하는 경우에만 첫 번째 함수 를 사용하려고 한다고 가정하십시오 . 가능하지만 가능하게하는 메커니즘은 복잡합니다. 그리고 이것은 매우 간단한 논리입니다. 더 복잡한 논리가 있으면 더 나빠집니다. 또한 CPS에서 많은 코드를 다시 작성해야 할 수도 있습니다. 그리고 여전히 가치 수준의 용어에 의존하는 유형 수준 표현이 없습니다
John L

아, 무슨 말인지 알 겠어 이것은 nty에 따라 다른 일을하는 vReplicate에서와 같이 Natty를위한 것입니다. 실제로 이것은 약간 혼란 스러울 수 있습니다. CPS 스타일의 대안은 다음과 같이 압축 된 실체를 사용하는 것 zeroes :: IO (Some (Flip Vec Int))입니다.
ulfnorell

19

pigworker는 왜 우리 종속 유형으로 향 해야하는지에 대한 훌륭한 토론을 제공합니다 . (b) 그들은 실제로 Haskell이 이미하고있는 많은 것을 단순화 할 것 입니다.

"왜 안돼?" 질문, 내가 생각하는 몇 가지 포인트가 있습니다. 첫 번째 요점은 종속 유형의 기본 개념은 쉽지만 (유형이 값에 따라 달라짐) 해당 기본 개념의 결과는 미묘하고 심오하다는 것입니다. 예를 들어, 값과 유형의 구별은 여전히 ​​유효합니다. 하지만 그들 사이의 차이를 토론하는 것은이됩니다 까지이는 Hindley--Milner 또는 System F에서보다 미묘한 차이가 있습니다. 이는 어느 정도 종속 유형이 근본적으로 어렵다는 사실 때문입니다 (예 : 1 차 논리는 ​​결정할 수 없음). 그러나 더 큰 문제는 실제로 무슨 일이 일어나고 있는지를 포착하고 설명하기에 좋은 어휘가 부족하다는 것입니다. 점점 더 많은 사람들이 의존형에 대해 배우면 더 나은 어휘를 개발하게되므로 근본적인 문제가 여전히 어려운 경우에도 상황을 이해하기가 더 쉬워 질 것입니다.

두 번째 요점은 Haskell이 성장하고 있다는 사실과 관련이 있습니다.종속 유형으로. 우리는 그 목표를 향한 점진적인 진전을 이루고 있지만 실제로 그 목표를 달성하지 못하면 점진적 패치 위에 점진적 패치가있는 언어가 붙어 있습니다. 새로운 아이디어가 대중화되면서 다른 언어에서도 같은 일이 일어났습니다. 자바는 (모수) 다형성을 위해 사용하지 않았다. 그들이 마침내 그것을 추가했을 때, 그것은 약간의 추상화 누출과 무너진 힘으로 점진적으로 개선되었습니다. 서브 타이핑과 다형성의 혼합은 본질적으로 어렵다; 그러나 이것이 Java Generics가 작동하는 방식이 아닙니다. 이전 버전의 Java를 점진적으로 향상시켜야한다는 제약 때문에 그들이하는 방식으로 작동합니다. OOP가 발명되고 사람들이 "객관적인"글을 쓰기 시작한 날에 더 뒤로 C (Objective-C와 혼동하지 말 것) 등. C ++은 C의 엄격한 수퍼 셋이라는 개념으로 시작했습니다. 새로운 패러다임을 추가하려면 항상 언어를 새로 정의하거나 복잡한 혼란으로 끝나야합니다. 이 모든 것의 요점은 Haskell에 진정한 종속 유형을 추가하는 데 언어를 재구성하고 재구성하는 데 어느 정도의 언어가 필요하다는 것입니다. 그러나 그러한 종류의 정밀 검사를 수행하는 것은 실제로 어려운 반면, 우리가 진행 한 점진적 진보는 단기적으로 저렴 해 보입니다. 실제로 GHC를 해킹하는 사람은 많지 않지만 살아남을 수있는 레거시 코드가 많이 있습니다. 이것이 DDC, Cayenne, Idris 등과 같은 스핀 오프 언어가 너무 많은 이유의 일부입니다. C ++는 C의 엄격한 수퍼 셋이라는 개념으로 시작했습니다. 새로운 패러다임을 추가하려면 항상 언어를 새로 정의하거나 복잡한 혼란으로 끝나야합니다. 이 모든 것의 요점은 Haskell에 진정한 종속 유형을 추가하는 데 언어를 재구성하고 재구성하는 데 어느 정도의 언어가 필요하다는 것입니다. 그러나 그러한 종류의 정밀 검사를 수행하는 것은 실제로 어려운 반면, 우리가 진행 한 점진적 진보는 단기적으로 저렴 해 보입니다. 실제로 GHC를 해킹하는 사람은 많지 않지만 살아남을 수있는 레거시 코드가 많이 있습니다. 이것이 DDC, Cayenne, Idris 등과 같은 스핀 오프 언어가 너무 많은 이유의 일부입니다. C ++는 C의 엄격한 수퍼 셋이라는 개념으로 시작했습니다. 새로운 패러다임을 추가하려면 항상 언어를 새로 정의하거나 복잡한 혼란으로 끝나야합니다. 이 모든 것의 요점은 Haskell에 진정한 종속 유형을 추가하는 데 언어를 재구성하고 재구성하는 데 어느 정도의 언어가 필요하다는 것입니다. 그러나 그러한 종류의 정밀 검사를 수행하는 것은 실제로 어려운 반면, 우리가 진행 한 점진적 진보는 단기적으로 저렴 해 보입니다. 실제로 GHC를 해킹하는 사람은 많지 않지만 살아남을 수있는 레거시 코드가 많이 있습니다. 이것이 DDC, Cayenne, Idris 등과 같은 스핀 오프 언어가 너무 많은 이유의 일부입니다. 아니면 복잡한 혼란으로 끝납니다. 이 모든 것의 요점은 Haskell에 진정한 종속 유형을 추가하는 데 언어를 재구성하고 재구성하는 데 어느 정도의 언어가 필요하다는 것입니다. 그러나 그러한 종류의 정밀 검사를 수행하는 것은 실제로 어려운 반면, 우리가 진행 한 점진적 진보는 단기적으로 저렴 해 보입니다. 실제로 GHC를 해킹하는 사람은 많지 않지만 살아남을 수있는 레거시 코드가 많이 있습니다. 이것이 DDC, Cayenne, Idris 등과 같은 스핀 오프 언어가 너무 많은 이유의 일부입니다. 아니면 복잡한 혼란으로 끝납니다. 이 모든 것의 요점은 Haskell에 진정한 종속 유형을 추가하는 데 언어를 재구성하고 재구성하는 데 어느 정도의 언어가 필요하다는 것입니다. 그러나 그러한 종류의 정밀 검사를 수행하는 것은 실제로 어려운 반면, 우리가 진행 한 점진적 진보는 단기적으로 저렴 해 보입니다. 실제로 GHC를 해킹하는 사람은 많지 않지만 살아남을 수있는 레거시 코드가 많이 있습니다. 이것이 DDC, Cayenne, Idris 등과 같은 스핀 오프 언어가 너무 많은 이유의 일부입니다. 이런 종류의 정밀 검사에 전념하기는 어렵지만, 우리가 진행 한 점진적인 진전은 단기간에 저렴 해 보입니다. 실제로 GHC를 해킹하는 사람은 많지 않지만 살아남을 수있는 레거시 코드가 많이 있습니다. 이것이 DDC, Cayenne, Idris 등과 같은 스핀 오프 언어가 너무 많은 이유의 일부입니다. 이런 종류의 정밀 검사에 전념하기는 어렵지만, 우리가 진행 한 점진적인 진전은 단기간에 저렴 해 보입니다. 실제로 GHC를 해킹하는 사람은 많지 않지만 살아남을 수있는 레거시 코드가 많이 있습니다. 이것이 DDC, Cayenne, Idris 등과 같은 스핀 오프 언어가 너무 많은 이유의 일부입니다.

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