유형 검사 및 재귀 유형 (Haskell / Ocaml에서 Y 결합기 작성)


21

Haskell의 맥락에서 Y 조합자를 설명 할 때, 일반적으로 간단한 구현은 재귀 유형으로 인해 Haskell에서 유형 검사를하지 않습니다.

예를 들어, Rosettacode의 :

The obvious definition of the Y combinator in Haskell canot be used
because it contains an infinite recursive type (a = a -> b). Defining
a data type (Mu) allows this recursion to be broken.

newtype Mu a = Roll { unroll :: Mu a -> a }

fix :: (a -> a) -> a
fix = \f -> (\x -> f (unroll x x)) $ Roll (\x -> f (unroll x x))

실제로“명백한”정의는 유형 점검을하지 않습니다.

λ> let fix f g = (\x -> \a -> f (x x) a) (\x -> \a -> f (x x) a) g

<interactive>:10:33:
    Occurs check: cannot construct the infinite type:
      t2 = t2 -> t0 -> t1
    Expected type: t2 -> t0 -> t1
      Actual type: (t2 -> t0 -> t1) -> t0 -> t1
    In the first argument of `x', namely `x'
    In the first argument of `f', namely `(x x)'
    In the expression: f (x x) a

<interactive>:10:57:
    Occurs check: cannot construct the infinite type:
      t2 = t2 -> t0 -> t1
    In the first argument of `x', namely `x'
    In the first argument of `f', namely `(x x)'
    In the expression: f (x x) a
(0.01 secs, 1033328 bytes)

Ocaml에도 동일한 제한이 있습니다.

utop # let fix f g = (fun x a -> f (x x) a) (fun x a -> f (x x) a) g;;
Error: This expression has type 'a -> 'b but an expression was expected of type 'a                                    
       The type variable 'a occurs inside 'a -> 'b

그러나 Ocaml에서는 -rectypes스위치 를 전달하여 재귀 유형을 허용 할 수 있습니다 .

   -rectypes
          Allow  arbitrary  recursive  types  during type-checking.  By default, only recursive
          types where the recursion goes through an object type are supported.

를 사용 -rectypes하면 모든 것이 작동합니다.

utop # let fix f g = (fun x a -> f (x x) a) (fun x a -> f (x x) a) g;;
val fix : (('a -> 'b) -> 'a -> 'b) -> 'a -> 'b = <fun>
utop # let fact_improver partial n = if n = 0 then 1 else n*partial (n-1);;
val fact_improver : (int -> int) -> int -> int = <fun>
utop # (fix fact_improver) 5;;
- : int = 120

타입 시스템과 타입 추론에 대해 궁금해해서 여전히 대답 할 수없는 몇 가지 질문이 제기됩니다.

  • 먼저 타입 체커는 타입을 t2 = t2 -> t0 -> t1어떻게 알 수 있습니까? 해당 유형을 생각해 낸 후 문제는 유형 ( t2)이 오른쪽에 자체를 참조한다는 것입니다.
  • 둘째, 아마도 가장 흥미로운 것은 Haskell / Ocaml 타입 시스템이 이것을 허용하지 않는 이유는 무엇입니까? Ocaml 은 스위치 가 주어지면 재귀 유형을 처리 할 있더라도 기본적으로 허용하지 않기 때문에 좋은 이유 있다고 생각 합니다.-rectypes

이것들이 정말로 큰 주제라면 관련 문헌에 대한 조언을 부탁드립니다.

답변:


16

먼저 GHC 오류

GHC는로 몇 가지 제약 조건을 통합하려고 시도하고 있습니다 x. 먼저이를 함수로 사용합니다.

x :: a -> b

다음으로 우리는 그것을 그 함수의 값으로 사용합니다

x :: a

그리고 마지막으로 우리는 그것을 원래의 인수 표현식으로 통합합니다.

x :: (a -> b) -> c -> d

그러나 이제는 x x통일을 시도 t2 -> t1 -> t0하지만 t2,의 첫 번째 인수 인 x와의 통일이 필요하므로 통일 할 수 없습니다 x. 따라서 우리의 오류 메시지.

다음은 일반적인 재귀 유형이 아닌 이유입니다. 주목할 가치가있는 첫 번째 요점은 equi와 iso 재귀 유형의 차이점입니다.

  • 동등 재귀는 예상 mu X . Type대로 임의로 확장하거나 접는 것과 같습니다.
  • 이소 타입 재귀 사업자의 쌍을 제공 fold하고 unfold타입의 재귀 정의 접어 전개한다.

이제 동등 재귀 유형은 이상적으로 들리지만 복잡한 유형 시스템에서는 올바르게 얻기가 어렵습니다. 실제로 유형 검사를 결정할 수 없게 만들 수 있습니다. 나는 OCaml 타입 시스템의 모든 세부 사항에 익숙하지 않지만 Haskell의 완전히 등가적인 타입은 타입 체커가 임의로 타입을 통합하려고 시도하면서 타입 검사기가 임의로 루프를 일으킬 수 있습니다. 기본적으로 Haskell은 타입 검사가 종료되도록합니다. 또한 Haskell에서 유형 동의어는 바보이며 가장 유용한 재귀 유형은 다음과 같이 정의 type T = T -> ()되지만 Haskell에서 거의 즉시 인라인되지만 재귀 유형을 인라인 할 수는 없습니다. 무한합니다! 따라서 Haskell의 재귀 유형은 동의어가 처리되는 방식에 대한 대대적 인 개편을 요구할 것입니다. 아마도 언어 확장으로도 노력할 가치가 없습니다.

아이소 재귀 유형은 사용하기가 다소 어려우므로 유형 검사기에게 유형을 접고 펼치는 방법을 명시 적으로 알려야하므로 프로그램을 읽고 쓰는 것이 더 복잡해집니다.

그러나 이것은 당신이 당신의 Mu유형으로 하는 것과 매우 유사 합니다. Roll접 히고 unroll펼쳐집니다. 실제로 우리는 iso-recursive 유형을 구워 넣었습니다. 그러나 equi-recursive 유형은 너무 복잡하므로 OCaml 및 Haskell과 같은 시스템은 유형 수준 수정 점을 통해 반복을 전달하도록합니다.

이제 관심이 있다면 타입과 프로그래밍 언어를 추천합니다. 내가 올바른 용어를 가지고 있는지 확인하기 위해 이것을 작성하면서 내 사본이 무릎에 열려 있습니다. :)


특히 21 장 좋은 유도, coinduction에 대한 직관, 재귀 유형 제공
다니엘 Gratzer

고맙습니다! 정말 매력적입니다. 저는 현재 TAPL을 읽고 있으며,이 내용이이 책의 뒷부분에서 다룰 것이라는 점을 기쁘게 생각합니다.
베타

@beta Yep, TAPL, 그리고 형과 프로그래밍 언어의 고급 주제는 훌륭한 자료입니다.
Daniel Gratzer

2

OCaml에서는 -rectypes매개 변수로 컴파일러 에 전달 하거나 #rectypes;;최상위 레벨에 입력해야 합니다. 대략적으로 말하면 통일 중에 "확인 발생"이 해제됩니다. 상황 The type variable 'a occurs inside 'a -> 'b은 더 이상 문제가되지 않습니다. 유형 시스템은 여전히 ​​"정확한"(소리 등) 유형이며, 유형으로 발생하는 무한 트리는 때때로 "합리적 트리"라고합니다. 타입 시스템이 약해집니다. 즉 일부 프로그래머 오류를 감지하는 것이 불가능 해집니다.

OCaml의 예제가 포함 된 고정 소수점 연산자에 대한 자세한 내용 은 람다 미적분학 (슬라이드 27에서 시작)에 대한 강의를 참조하십시오 .

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