簡體   English   中英

GHC發生檢查識別哪些情況?

[英]What cases do the GHC occurs check identify?

GHC 發生檢查可防止您構造無限類型。 它的目的是防止代碼中的常見錯誤或防止類型檢查器無限循環,還是兩者兼而有之?

它識別哪些情況,惡意用戶是否有可能欺騙它(如在 Safe Haskell 上下文中)進入循環? 如果類型系統是圖靈完備的(是嗎?)我不明白 GHC 如何保證計算將停止。

將類型推斷視為求解方程組。 讓我們看一個例子:

f x = (x,2)

我們如何推斷f的類型? 我們看到它是一個函數:

f :: a -> b

此外,從f的結構中我們可以看到以下等式類似地成立:

b = (c,d)
d = Int
c = a

通過求解這個方程組,我們可以看到f的類型是a -> (a, Int) 現在讓我們看看以下(錯誤的)函數:

f x = x : x

(:)的類型是a -> [a] -> [a] ,因此生成以下方程組(簡化):

a = a
a = [a]

所以我們得到一個方程a = [a] ,從中我們可以得出結論,這個方程組沒有解,因此代碼不是很好的類型。 如果我們不拒絕方程a = [a] ,我們確實會進入一個無限循環添加方程a = [a]a = [[a]]a = [[[a]]]等我們的系統(或者,正如 Daniel 在他的回答中指出的那樣,我們可以在我們的類型系統中允許無限類型,但這會導致錯誤的程序,例如fx = x : x進行類型檢查)。

你也可以在 ghci 中測試:

> let f x = x : x

<interactive>:2:15:
    Occurs check: cannot construct the infinite type: a0 = [a0]
    In the second argument of `(:)', namely `x'
    In the expression: x : x
    In an equation for `f': f x = x : x

至於您的其他問題:GHC Haskell 的類型系統不是圖靈完備的,並且類型檢查器保證會停止 - 除非您啟用UndecidableInstances ,在這種情況下,理論上它可以進入無限循環。 然而,GHC 通過具有固定深度的遞歸堆棧來確保終止,因此不可能構建一個永不停止的程序編輯:根據 CAMcCann 的評論,這畢竟是可能的 - 在允許在不增加堆棧深度的情況下循環的類型級別)。

然而,有可能使編譯花費任意長的時間,因為 Hindley-Milner 類型推斷的復雜性在最壞的(但不是平均的!)情況下是指數級的:

dup x = (x,x)

bad = dup . dup . dup . dup . dup . dup . dup . dup
      . dup . dup . dup . dup . dup . dup . dup . dup
      . dup . dup . dup . dup . dup . dup . dup . dup
      . dup . dup . dup . dup . dup . dup . dup . dup

Safe Haskell 不會保護您免受此影響 - 如果您想允許潛在的惡意用戶在您的系統上編譯 Haskell 程序,請查看mueval

GHC 發生檢查可防止您構造無限類型。

這僅在防止語法上無限的類型的字面意義上才是正確的。 這里真正發生的只是一種遞歸類型,在某種意義上,統一算法需要內聯遞歸。

通過使遞歸顯式,總是可以定義完全相同的類型。 這甚至可以通用地完成,使用類型級別的fix

newtype Fix f = Fix (f (Fix f))

例如,類型Fix ((->) a)等效於將b(a -> b)統一。

然而,在實踐中,“無限類型”錯誤幾乎總是表示代碼中的錯誤(因此如果它壞了,你可能不應該Fix它)。 通常的情況是混淆參數順序或在不使用顯式類型簽名的代碼中以其他方式在錯誤的位置使用表達式。

一個以正確的方式極其幼稚的類型推斷系統可能會擴展遞歸,直到它耗盡內存,但停止問題不會進入它——如果一個類型需要與自身的一部分統一,那永遠不會發生工作(至少在 Haskell 中,可能有一些語言將其視為等同於上面的顯式遞歸newtype )。

GHC 中的類型檢查和類型推斷都不是圖靈完備的,除非您啟用UndecidableInstances ,在這種情況下,您可以通過函數依賴項或類型系列進行任意計算。

Safe Haskell 根本沒有真正進入畫面。 很容易生成非常大的推斷類型,盡管它是有限的,但會耗盡系統內存,如果內存為我服務,Safe Haskell 無論如何都不會限制使用UndecidableInstances

我的書簽中以下精彩郵件:無限類型沒有錯! 即使使用無限類型,也沒有使類型檢查器循環的真正危險。 類型系統不是圖靈完備的(除非您明確要求它具有像UndecidableInstances擴展這樣的東西)。

目的(在 Haskell 編譯器中)是為了防止代碼中的常見錯誤。 可以構建一個支持類型無限遞歸的類型檢查器和推理引擎。 這個問題中有一些進一步的信息。

OCaml 使用-rectypes實現遞歸類型,所以這絕對是可能的。 OCaml 社區將更加精通出現的一些問題(默認情況下該行為是關閉的)。

發生檢查標識無限類型擴展。 例如,這段代碼:

Prelude> let a = [a]
<interactive>:2:10:
    Occurs check: cannot construct the infinite type: t0 = [t0]
    In the expression: a
    In the expression: [a]
    In an equation for `a': a = [a]

如果您嘗試手動計算類型,則類型為[ [ [ [ ... ] ] ] ] 手工編寫這樣的類型是不可能的,因為它們是無限的。

發生檢查發生在類型推斷期間,這是與類型檢查不同的階段。 必須推斷無限類型,因為它們不能手動注釋。 Haskell-98 類型推斷是可判定的,因此不可能誘使編譯器進入循環(當然,我懷疑這個示例利用了錯誤)。 默認情況下,GHC 使用系統 F 的受限子集,其類型推斷也是可判定的。 通過一些擴展,例如RankNTypes ,GHC 確實允許類型推斷不可判定的代碼,但隨后需要類型注釋,因此再次沒有類型推斷階段循環的危險。

由於圖靈完備語言是不可判定的,所以默認的類型系統不能是圖靈完備的。 我不知道 GHC 的類型系統是否在啟用某些擴展組合的情況下成為圖靈完備的,但某些擴展(例如UndecidableInstances )允許編寫代碼,這些代碼會使編譯器因堆棧溢出而崩潰。

順便說一句,禁用發生檢查的主要問題是許多常見的編碼錯誤會導致無限類型的錯誤,因此禁用它通常會導致比解決的問題更多。 如果您確實打算使用無限類型,則 newtype 包裝器將允許它沒有太多多余的符號。

暫無
暫無

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

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