簡體   English   中英

Haskell如何進行類型檢查無限遞歸值?

[英]How does Haskell type-check infinite recursive values?

定義此數據類型:

data NaturalNumber = Zero | S NaturalNumber
    deriving (Show)

在Haskell中(使用GHC編譯),此代碼將在沒有警告或錯誤的情況下運行:

infinity = S infinity
inf1 = S inf2
inf2 = S inf1

因此,遞歸和相互遞歸的無限深度值都會通過類型檢查。

但是,以下代碼給出了錯誤:

j = S 'h'

錯誤狀態Couldn't match expected type 'NaturalNumber' with actual type 'Char' 即使我設置,(相同)錯誤仍然存​​在

j = S (S (S (S ... (S 'h')...)))

有一百個左右的嵌套S

Haskell如何判斷infinityNaturalNumber的有效成員,但j不是?

有趣的是,它還允許:

bottom = bottom
k = S bottom

Haskell只是試圖證明一個程序的不正確性,如果它不能這樣做,那么允許它嗎? 或者Haskell的類型系統不是圖靈完整的,所以如果它允許程序那么程序可證明(在類型級別)正確嗎?

(如果類型系統(在Haskell的形式語義中,而不僅僅是類型檢查器)是Turing完成的,那么它將無法實現某些正確類型的程序是正確的,或者某些錯誤輸入的程序是不正確的,因為它的不可判定性停止問題。)

S :: NaturalNumber -> NaturalNumber

infinity = S infinity

我們首先假設什么:我們為infinity分配一些未解決的類型_a並試圖找出它是什么。 我們知道我們已經將S應用於infinity ,所以_a必須是構造函數類型中的箭頭左側的任何內容,即NaturalNumber 我們知道infinity是應用S的結果,所以infinity :: NaturalNumber ,再次(如果我們為這個定義得到兩個沖突的類型,我們必須發出一個類型錯誤)。

類似的推理適用於相互遞歸的定義。 inf1必須是NaturalNumber因為它在inf2顯示為S的參數; inf2必須是NaturalNumber因為它是S的結果; 等等

通用算法是分配定義未知類型(值得注意的例外是文字和構造函數),然后通過查看每個定義的使用方式來創建對這些類型的約束。 例如,這必須是某種形式的列表,因為它是reverse ,並且這必須是一個Int因為它用於從IntMap查找值等。

如果是

oops = S 'a'

'a' :: Char因為它是一個文字,但是,我們也必須有'a' :: NaturalNumber因為它被用作S的參數。 我們得到明顯的偽造約束,即文字的類型必須都是CharNaturalNumber ,這會導致類型錯誤。

並在

bottom = bottom

我們從bottom :: _a開始。 唯一的約束是_a ~ _a ,因為正在使用類型_abottom )的值,其中期望類型為_a的值(在bottom定義的RHS上)。 由於沒有什么可以進一步約束類型,因此未解決的類型變量是通用的 :它被通用量詞綁定以生成bottom :: forall a. a bottom :: forall a. a

注意在推斷bottom類型時,上面bottom兩種用法如何具有相同的類型( _a )。 這打破了多態遞歸:其定義中每次出現的值都與定義本身的類型相同。 例如

-- perfectly balanced binary trees
data Binary a = Leaf a | Branch (Binary (a, a))
-- headB :: _a -> _r
headB (Leaf x) = x -- _a ~ Binary _r; headB :: Binary _r -> _r
headB (Branch bin) = fst (headB bin)
-- recursive call has type headB :: Binary _r -> _r
-- but bin :: Binary (_r, _r); mismatch

所以你需要一個類型簽名:

headB :: {-forall a.-} Binary a -> a
headB (Leaf x) = x
headB (Branch bin) = fst (headB {-@(a, a)-} bin)
-- knowing exactly what headB's signature is allows for polymorphic recursion

所以:當某些東西沒有類型簽名時,類型檢查器會嘗試為它分配一個類型,如果它遇到偽造的約束,它會拒絕該程序。 當某些東西有一個類型簽名時,類型檢查器會進入它,以確保它是正確的(如果你更願意這樣想的話,試圖證明它是錯誤的)。

Haskell的類型系統不是Turing完整的,因為有很多語法限制來防止例如類型lambdas(沒有語言擴展),但它不足以確保所有程序運行完成而沒有錯誤,因為它仍然允許底部(更不用說所有不安全的功能)。 它提供了較弱的保證,如果程序在不使用不安全功能的情況下運行完成,它將保持類型正確。 在GHC下,通過足夠的語言擴展,類型系統確實成為​​圖靈完整的。 我不認為它允許通過不良類型的程序; 我認為你能做的最多就是將編譯器拋入無限循環。

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM