2012-04-27 64 views
17

以下定義:哈斯克爾級2個位由於編譯錯誤

import Control.Monad.ST 
import Data.STRef 

fourty_two = do 
    x <- newSTRef (42::Int) 
    readSTRef x 

GHC在以下編譯:

main = (print . runST) fourty_two -- (1) 

但這並不:

main = (print . runST) $ fourty_two -- (2) 

但隨後由於bdonlan在評論中指出,這確實編譯:

main = ((print . runST) $) fourty_two -- (3) 

但是,這並不編譯

main = (($) (print . runST)) fourty_two -- (4) 

這似乎表明,(3)只編譯由於綴$的特殊待遇,但是,它仍然沒有解釋爲什麼(1)沒有編譯。

問題:

1)我已經閱讀了以下兩個問題(firstsecond),我已經被引導相信$只能與單態類型的實例化。但是我會同樣假設.只能用單形類型實例化,結果也會失敗。 爲什麼第一個代碼成功,但第二個代碼卻不成功? (例如是否有一個特殊的規則GHC具有用於第一種情況下,它不能在第二應用?)

2)是否有編譯所述第二代碼的電流GHC延伸? (也許ImpredicativePolymorphism這樣做在某些時候,但似乎過時了,有什麼替代呢?)

3)是否有任何的方式來定義說`my_dollar`使用GHC的擴展做什麼$的做法,但也能夠處理多態類型,所以(print . runST) `my_dollar` fourty_two編譯?

編輯:建議的答覆:

而且,下面的編譯失敗:

main = ((.) print runST) fourty_two -- (5) 

這是一樣的(1),除了不使用的.中綴版本。

結果,似乎GHC有兩個$.特殊的規則,但只有他們中綴版本。

+2

爲了使它更有趣,'((打印runST)$)。fourty_two' _does_工作 – bdonlan 2012-04-27 06:33:47

+0

這很有趣,而且進一步混淆的東西! – Clinton 2012-04-27 06:37:39

+0

我相當肯定GHC有一個特殊的規則來支持'runST $ do'這個例子,但我現在找不到一個引用。 – 2012-04-27 07:21:23

回答

6
  1. 我不知道我明白爲什麼第二個不起作用。我們可以查看print . runST的類型,並觀察它具有足夠的多態性,因此責任不在於(.)。我懷疑GHC對中藥($)的特殊規定還不夠。如果你提出這個片段作爲他們跟蹤器的bug,SPJ和朋友可能會重新檢查它。

    至於爲什麼第三個例子的作品,好,這只是因爲再次的((print . runST) $)種類足夠多態的;實際上,它等於print . runST的類型。

  2. 沒有取代ImpredicativePolymorphism,因爲GHC人們還沒有看到任何使用情況下,額外的編程方便壓倒編譯器錯誤額外的潛力。 (我不認爲他們會認爲這是令人信服的,要麼,但當然我不是權威。)
  3. 我們可以定義一個略少多態($$)

    {-# LANGUAGE RankNTypes #-} 
    infixl 0 $$ 
    ($$) :: ((forall s. f s a) -> b) -> ((forall s. f s a) -> b) 
    f $$ x = f x 
    

    然後你的榜樣typechecks好這一新的運營商:

    *Main> (print . runST) $$ fourty_two 
    42 
    
0

我不能在這個問題上過多的權威說,但這裏是我認爲可能會發生:

考慮什麼typechecker在每種情況下做的。 (print . runST)有型號Show b => (forall s. ST s t) -> IO()fourty_twoST x Int

forall這裏是一個存在的類型修飾符 - 這意味着傳入的參數必須是對s普遍。也就是說,您必須通過支持s任何值的多態類型。如果你沒有明確說明forall,Haskell把它放在類型定義的最外層。這意味着,fourty_two :: forall x. ST x Int(print . runST) :: forall t. Show t => (forall s. ST s t) -> IO()

現在,我們可以通過讓t = Int, x = s匹配forall x. ST x Intforall s. ST s t。所以直接呼叫案件的作品。但是,如果我們使用$會發生什麼?

$具有類型($) :: forall a b. (a -> b) -> a -> b。當我們解決ab,由於$類型沒有任何明確的類型的範圍是這樣的fourty_twox參數被吊出在類型最外層的餘地($) - 所以($) :: forall x t. (a = forall s. ST s t -> b = IO()) -> (a = ST x t) -> IO()。此時,它會嘗試匹配ab,並失敗。

如果改爲寫((print . runST) $) fourty_two,則編譯器首先解決的((print . runST $)類型。它解決了($)的類型爲forall t. (a = forall s. ST s t -> b = IO()) -> a -> b;請注意,由於a的第二次出現是不受限制的,所以我們沒有那種討厭的類型變量泄漏到最外層的範圍!因此匹配成功,函數部分應用,表達式的整體類型爲forall t. (forall s. ST s t) -> IO(),這就是我們開始的地方,所以它成功了。

+0

在第二個最後一段中,我不明白爲什麼'x'被「解除」。不是'x'是編譯器創建的實際類型嗎?據我所知,這是一種我們永遠無法訪問的隱藏類型,但它就像說'Int'一樣。我們不舉起'詮釋',爲什麼舉起'x'? – Clinton 2012-04-27 07:36:03

+0

即實際上'X''實際上'X12345',編譯器生成的實際類型(儘管我們永遠無法訪問它)? – Clinton 2012-04-27 07:36:42

+0

@Clinton,'x'不是一個類型,它是一個類型變量。我不確定爲什麼它選擇將'fourty_two'上的隱式量化提升到'... $ ...'表達式類型的外層。但是,您不能提升'Int'的量化,因爲這不是一個類型變量,而是一種類型。 – bdonlan 2012-04-28 02:18:05