[英]How do I prove type-level list properties in haskell?
我有這些類型的家庭:
type family xs ++ ys where
'[] ++ ys = ys
(x : xs) ++ ys = x : (xs ++ ys)
type family Drop n xs where
Drop O xs = xs
Drop (S n) (_ : xs) = Drop n xs
type family Length xs where
Length '[] = O
Length (x : xs) = S (Length xs)
在某些時候,GHC 想要證明
forall a. Drop (Length a) (a ++ c) ~ c
我曾經把它推到一些構造函數的上下文中。
我如何普遍證明這個屬性?
好的,所以你的類型家庭很好,你的財產幾乎是正確的。
你要證明的是:
proof :: Drop (Length a) (a ++ c) :~: c
除非你真的不知道a
和c
是什么。 它們是隱式量化的。 您希望它們是明確的,以便我們可以對它們進行歸納。
proof :: (a :: [ k ]) -> (c :: [ k ]) -> Drop (Length a) (a ++ c) :~: c
您會意識到這不會進行類型檢查,因為 Haskell 沒有真正的依賴類型,但有一種解決方法:單例類型。 這個想法是創建一個索引類型,以便每個術語對應於用作類型索引的不同類型的一個術語。 我知道這聽起來有點令人困惑,但示例應該可以澄清它。
您可以使用singletons
庫或從頭開始構建它們,這就是我在這里要做的。
data family Sing (x :: k)
data SList xs where
SNil :: SList '[]
SCons :: Sing x -> SList xs -> SList (x ': xs)
這里Sing
是一個數據族,因此我可以泛指具有單例的事物。 SList
是列表類型的單例版本,如您所見, SNil
構造函數對應於類型級別[]
。 同樣, SCons
反映:
。
然后(假設您在某處也有data Nat = O | S Nat
的定義)您所追求的證明的簽名是
proof :: SList a -> SList c -> Drop (Length a) (a ++ c) :~: c
請注意,我將您的~
更改為:~:
這是Data.Type.Equality
可用的類型運算符。 唯一的構造函數是Refl
,只有當它的兩個操作數完全相同時才能斷言。
現在我們只需要證明它。 幸運的是,這是一個超級簡單的性質來證明,你只需要對SList a
在基本情況下SList a
是SNil
,所以你真的想證明Drop (Length '[]) ('[] '++ c) :~: c
。 因為您使用了類型系列,類型檢查器會立即將其減少為c :~: c
。 由於兩個操作數相同,我們可以使用Refl
構造函數來證明這種情況。
proof SNil _ = Refl
現在是歸納案例。 我們將再次進行模式匹配,這一次了解到SList a
的形式為SCons a as
a :: Sing x
和as :: Sing xs
。 這意味着我們需要證明的是Drop (Length (x ': xs)) ((x : xs) ++ c) :~: c
。 同樣,您的類型系列將立即開始進行計算並將此目標減少到Drop (Length xs) (xs ++ c) :~: c
因為它實際上不需要知道x
是什么來進行評估。
事實證明, proof as c
(nb。我使用as
而不是SCons a as
) 恰好具有所需的類型,因此我們使用它來證明屬性。
這是完整的證據。
proof :: SList a -> SList c -> Drop (Length a) (a ++ c) :~: c
proof SNil _ = Refl
proof (SCons a as) cs = proof as cs
為了使這些工作,您需要所有這些語言擴展:
{-# LANGUAGE GADTs #-}
{-# LANGUAGE DataKinds #-}
{-# LANGUAGE PolyKinds #-}
{-# LANGUAGE TypeFamilies #-}
{-# LANGUAGE TypeOperators #-}
{-# LANGUAGE KindSignatures #-}
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.