Resolusi jenis lubang tidak menentu

105

Baru-baru ini saya menemukan bahwa lubang tipe yang dikombinasikan dengan pencocokan pola pada bukti memberikan pengalaman seperti Agda yang cukup bagus di Haskell. Sebagai contoh:

{-# LANGUAGE
    DataKinds, PolyKinds, TypeFamilies, 
    UndecidableInstances, GADTs, TypeOperators #-}

data (==) :: k -> k -> * where
    Refl :: x == x

sym :: a == b -> b == a
sym Refl = Refl 

data Nat = Zero | Succ Nat

data SNat :: Nat -> * where
    SZero :: SNat Zero
    SSucc :: SNat n -> SNat (Succ n)

type family a + b where
    Zero   + b = b
    Succ a + b = Succ (a + b)

addAssoc :: SNat a -> SNat b -> SNat c -> (a + (b + c)) == ((a + b) + c)
addAssoc SZero b c = Refl
addAssoc (SSucc a) b c = case addAssoc a b c of Refl -> Refl

addComm :: SNat a -> SNat b -> (a + b) == (b + a)
addComm SZero SZero = Refl
addComm (SSucc a) SZero = case addComm a SZero of Refl -> Refl
addComm SZero (SSucc b) = case addComm SZero b of Refl -> Refl
addComm sa@(SSucc a) sb@(SSucc b) =
    case addComm a sb of
        Refl -> case addComm b sa of
            Refl -> case addComm a b of
                Refl -> Refl 

Hal yang sangat menyenangkan adalah saya dapat mengganti sisi kanan Refl -> expkonstruksi dengan lubang tipe, dan tipe target lubang saya diperbarui dengan buktinya, sama seperti dengan rewriteformulir di Agda.

Namun, terkadang lubang gagal diperbarui:

(+.) :: SNat a -> SNat b -> SNat (a + b)
SZero   +. b = b
SSucc a +. b = SSucc (a +. b)
infixl 5 +.

type family a * b where
    Zero   * b = Zero
    Succ a * b = b + (a * b)

(*.) :: SNat a -> SNat b -> SNat (a * b)
SZero   *. b = SZero
SSucc a *. b = b +. (a *. b)
infixl 6 *.

mulDistL :: SNat a -> SNat b -> SNat c -> (a * (b + c)) == ((a * b) + (a * c))
mulDistL SZero b c = Refl
mulDistL (SSucc a) b c = 
    case sym $ addAssoc b (a *. b) (c +. a *. c) of
        -- At this point the target type is
        -- ((b + c) + (n * (b + c))) == (b + ((n * b) + (c + (n * c))))
        -- The next step would be to update the RHS of the equivalence:
        Refl -> case addAssoc (a *. b) c (a *. c) of
            Refl -> _ -- but the type of this hole remains unchanged...

Juga, meskipun jenis target tidak selalu berbaris di dalam bukti, jika saya menempelkan semuanya dari Agda, itu masih diperiksa dengan baik:

mulDistL' :: SNat a -> SNat b -> SNat c -> (a * (b + c)) == ((a * b) + (a * c))
mulDistL' SZero b c = Refl
mulDistL' (SSucc a) b c = case
    (sym $ addAssoc b (a *. b) (c +. a *. c),
    addAssoc (a *. b) c (a *. c),
    addComm (a *. b) c,
    sym $ addAssoc c (a *. b) (a *. c),
    addAssoc b c (a *. b +. a *. c),
    mulDistL' a b c
    ) of (Refl, Refl, Refl, Refl, Refl, Refl) -> Refl

Apakah Anda punya ide mengapa ini terjadi (atau bagaimana saya bisa melakukan proof rewriting dengan cara yang kuat)?

András Kovács
sumber
8
Apa kau tidak berharap terlalu banyak? Pencocokan pola pada bukti kesetaraan adalah membangun persamaan (dua arah). Sama sekali tidak jelas di mana dan ke arah mana Anda ingin menerapkannya pada jenis target. Misalnya, Anda dapat menghilangkan sympanggilan masuk mulDistL'dan kode Anda akan tetap memeriksa.
kosmikus
1
Sangat mungkin saya berharap terlalu banyak. Namun, dalam banyak kasus, ini berfungsi seperti di Agda sehingga akan tetap berguna untuk mengetahui keteraturan perilaku. Saya tidak optimis meskipun, karena masalah tersebut kemungkinan besar terkait dengan isi perut pemeriksa tipe.
András Kovács
1
Ini sedikit ortogonal untuk pertanyaan Anda, tetapi Anda dapat melakukan pembuktian ini dengan menggunakan sekumpulan kombinator penalaran persamaan à la Agda. Cf. ini bukti konsep
gallais

Jawaban:

1

Jika Anda ingin menghasilkan semua nilai yang memungkinkan, maka Anda dapat menulis fungsi untuk melakukannya, baik dengan batas yang disediakan atau ditentukan.

Sangat mungkin untuk menggunakan Angka Gereja tingkat-tipe atau semacamnya untuk memaksakan pembuatan ini, tetapi hampir pasti terlalu banyak pekerjaan untuk apa yang mungkin Anda inginkan / butuhkan.

Ini mungkin bukan yang Anda inginkan (yaitu "Kecuali menggunakan hanya (x, y) karena z = 5 - x - y") tetapi ini lebih masuk akal daripada mencoba untuk memiliki semacam pembatasan yang dipaksakan pada tingkat tipe untuk mengizinkan nilai-nilai.

Billykart
sumber
-3

Itu terjadi karena nilai ditentukan pada waktu berjalan. Ini dapat membawa transformasi nilai tergantung pada apa yang dimasukkan pada waktu berjalan sehingga mengasumsikan bahwa lubang sudah diperbarui.

ajay
sumber
3
Tentu saja nilai ditentukan saat runtime, tetapi tidak ada hubungannya dengan pertanyaan. Informasi yang diperlukan sudah tersedia dari pencocokan pola di GADT.
int_index