[haskell] 이 GHC 핵심 “증거”를 읽는 방법?

GHC가 자연수의 경우 짝수를 반으로 만 줄일 수 있음을 어떻게 증명하는지 알아 내기 위해이 작은 하스켈을 썼습니다.

{-# LANGUAGE DataKinds, GADTs, KindSignatures, TypeFamilies #-}
module Nat where

data Nat = Z | S Nat

data Parity = Even | Odd

type family Flip (x :: Parity) :: Parity where
  Flip Even = Odd
  Flip Odd  = Even

data ParNat :: Parity -> * where
  PZ :: ParNat Even
  PS :: (x ~ Flip y, y ~ Flip x) => ParNat x -> ParNat (Flip x)

halve :: ParNat Even -> Nat
halve PZ     = Z
halve (PS a) = helper a
  where helper :: ParNat Odd -> Nat
        helper (PS b) = S (halve b)

핵심의 관련 부분은 다음과 같습니다.

Nat.$WPZ :: Nat.ParNat 'Nat.Even
Nat.$WPZ = Nat.PZ @ 'Nat.Even @~ <'Nat.Even>_N

Nat.$WPS
  :: forall (x_apH :: Nat.Parity) (y_apI :: Nat.Parity).
     (x_apH ~ Nat.Flip y_apI, y_apI ~ Nat.Flip x_apH) =>
     Nat.ParNat x_apH -> Nat.ParNat (Nat.Flip x_apH)
Nat.$WPS =
  \ (@ (x_apH :: Nat.Parity))
    (@ (y_apI :: Nat.Parity))
    (dt_aqR :: x_apH ~ Nat.Flip y_apI)
    (dt_aqS :: y_apI ~ Nat.Flip x_apH)
    (dt_aqT :: Nat.ParNat x_apH) ->
    case dt_aqR of _ { GHC.Types.Eq# dt_aqU ->
    case dt_aqS of _ { GHC.Types.Eq# dt_aqV ->
    Nat.PS
      @ (Nat.Flip x_apH)
      @ x_apH
      @ y_apI
      @~ <Nat.Flip x_apH>_N
      @~ dt_aqU
      @~ dt_aqV
      dt_aqT
    }
    }

Rec {
Nat.halve :: Nat.ParNat 'Nat.Even -> Nat.Nat
Nat.halve =
  \ (ds_dJB :: Nat.ParNat 'Nat.Even) ->
    case ds_dJB of _ {
      Nat.PZ dt_dKD -> Nat.Z;
      Nat.PS @ x_aIX @ y_aIY dt_dK6 dt1_dK7 dt2_dK8 a_apK ->
        case a_apK
             `cast` ((Nat.ParNat
                        (dt1_dK7
                         ; (Nat.Flip (dt2_dK8 ; Sym dt_dK6))_N
                         ; Nat.TFCo:R:Flip[0]))_R
                     :: Nat.ParNat x_aIX ~# Nat.ParNat 'Nat.Odd)
        of _
        { Nat.PS @ x1_aJ4 @ y1_aJ5 dt3_dKa dt4_dKb dt5_dKc b_apM ->
        Nat.S
          (Nat.halve
             (b_apM
              `cast` ((Nat.ParNat
                         (dt4_dKb
                          ; (Nat.Flip
                               (dt5_dKc
                                ; Sym dt3_dKa
                                ; Sym Nat.TFCo:R:Flip[0]
                                ; (Nat.Flip (dt_dK6 ; Sym dt2_dK8))_N
                                ; Sym dt1_dK7))_N
                          ; Sym dt_dK6))_R
                      :: Nat.ParNat x1_aJ4 ~# Nat.ParNat 'Nat.Even)))
        }
    }
end Rec }

Flip 유형 패밀리의 인스턴스를 통해 유형을 캐스팅하는 일반적인 흐름을 알고 있지만 완전히 따를 수없는 몇 가지 사항이 있습니다.

  • 의 의미는 @~ <Nat.Flip x_apH>_N무엇입니까? x의 Flip 인스턴스입니까? 그게 어떻게 다릅니 @ (Nat.Flip x_apH)까? 나는 둘 다 관심이 < >있고_N

첫 캐스트 관련 halve:

  • 무엇을 dt_dK6, dt1_dK7그리고 dt2_dK8약자? 나는 그것들이 일종의 동등성 증명이라는 것을 이해합니다. 그러나 어떤 것이 있습니까?
  • 나는 Sym등가를 거꾸로 통과 한다는 것을 이해합니다.
  • 의 역할은 무엇입니까 ;? 동등성 증명이 순차적으로 적용됩니까?
  • 이것들 _N_R접미사 는 무엇입니까 ?
  • 인가 TFCo:R:Flip[0]TFCo:R:Flip[1]플립의 경우?



답변

@~ 강제 적용입니다.

꺾쇠 괄호는 밑줄이 그어진 문자에 의해 주어진 역할과 함께 포함 된 유형의 반사 강제를 나타냅니다.

따라서 명목상 (단지 동일한 표현이 아닌 동일한 유형 )과 동일한 <Nat.Flip x_ap0H>_N동등 증명입니다 .Nat.Flip x_apHNat.Flip x_apH

추신에는 많은 논쟁이 있습니다. 스마트 생성자를 살펴보면 $WPS처음 두 개가 각각 x와 y 유형임을 알 수 있습니다. 생성자 인수가 Flip x(이 경우에는 Flip x ~ Even. 그런 다음 증명 x ~ Flip yy ~ Flip x. 최종 인수는 ParNat x.

이제 첫 번째 유형의 캐스트를 살펴 보겠습니다. Nat.ParNat x_aIX ~# Nat.ParNat 'Nat.Odd

우리는 (Nat.ParNat ...)_R. 이것은 유형 생성자 응용 프로그램입니다. 의 증명 x_aIX ~# 'Nat.OddNat.ParNat x_aIX ~# Nat.ParNat 'Nat.Odd. R수단은이 representationally 종류가 동형하지만 동일하지 않은 것을 의미하지 않습니다 (이 경우 그들은 동일하지만 우리는 캐스트를 수행하는 지식이 필요하지 않습니다).

이제 우리는 증명의 본문을 봅니다 (dt1_dK7 ; (Nat.Flip (dt2_dK8 ; Sym dt_dK6))_N; Nat.TFCo:R:Flip[0]). ;즉, 이러한 증명을 순서대로 적용하는 것입니다.

dt1_dK7의 증거입니다 x_aIX ~# Nat.Flip y_aIY.

우리가 (dt2_dK8 ; Sym dt_dK6). dt2_dK8y_aIY ~# Nat.Flip x_aIX. dt_dK6유형 'Nat.Even ~# Nat.Flip x_aIX입니다. 그래서 Sym dt_dK6유형 Nat.Flip x_aIX ~# 'Nat.Even이고 (dt2_dK8 ; Sym dt_dK6)유형입니다y_aIY ~# 'Nat.Even

따라서 (Nat.Flip (dt2_dK8 ; Sym dt_dK6))_N증거가있다 Nat.Flip y_aIY ~# Nat.Flip 'Nat.Even.

Nat.TFCo:R:Flip[0]뒤집기의 첫 번째 규칙입니다 Nat.Flip 'Nat.Even ~# 'Nat.Odd'.

이것들을 합치면 우리가 얻는 (dt1_dK7 ; (Nat.Flip (dt2_dK8 ; Sym dt_dK6))_N; Nat.TFCo:R:Flip[0])유형이 x_aIX #~ 'Nat.Odd있습니다.

두 번째로 복잡한 캐스트는 해결하기가 조금 더 어렵지만 동일한 원리로 작동해야합니다.


답변