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

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

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

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

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



답변

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

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


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

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


답변

의존적으로 입력 된 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의 종류 a
b구문이 일치하지 않는 (하지만, 동일한 검증을 할 수있다). 그것은 의존성을 훨씬 쉽게 다루는 오래된 기술입니다 (지난 세기의 논문에서). 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이 종속 유형을 향한 발걸음의 결과로 사람들이 무엇을하고 있는지 살펴보면 현재 언어의 발전을 통해 많은 이점을 얻을 수 있습니다.


답변

존은 종속 유형에 대한 또 다른 일반적인 오해입니다. 데이터가 런타임에만 사용 가능할 때 작동하지 않는다는 것입니다. 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의 답변에 대한 의견으로 생각되었습니다. 나는 분명히 실패합니다.


답변

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 등과 같은 스핀 오프 언어가 너무 많은 이유의 일부입니다.


답변