2015-09-06 65 views
3

我想使用Debug.Trace.trace來找出一個函數被評估多少次,並看到一些令人驚訝的結果。在這些表達式的評估中奇怪的重用

ghci> import Debug.Trace 

ghci> let x = (trace " Eval'd!" (* 2)) 3 in (x, x) 
(Eval'd! 
6, Eval'd! 
6) 

ghci> let x = (trace " Eval'd!" (* 2)) 3 in x `seq` (x, x) 
Eval'd! 
(Eval'd! 
6, Eval'd! 
6) 

ghci> let x = (trace " Eval'd!" (* 2)) (3 :: Int) in (x, x) 
(Eval'd! 
6,6) 

我做的是Eval'd是爲(* 2)功能的各評價一次印刷的假設。這是一個正確的假設嗎?

其次,爲什麼該功能曾多次打印過?我懷疑x屬於某種未指定類型的Num類型類型與它有關,因爲第三種情況有效,但我不能想到解釋。

(x, x) :: Num a => (a, a)保證元組的兩個元素具有相同的值,就像(x, x) :: (Int, Int)一樣多,那麼爲什麼eval是x兩倍?

UPDATE:

其實我曾以爲的(x, x)類型是Num a => (a, a)。但它顯然是(x, x) :: (Num t, Num t1) => (t, t1)

爲什麼GHC沒有意識到t ~ t1在這裏?我懷疑這與我的問題的答案有關。

回答

3

他們保證是同一類型:

Prelude Debug.Trace> :t let x = (trace " Eval'd!" (* 2)) 3 in (x, x) 
let x = (trace " Eval'd!" (* 2)) 3 in (x, x) 
    :: (Num t, Num t1) => (t, t1) 

另外,如果你把它放在一個文件,則僅被評估一次,從GHCI叫時也是如此。 (這是因爲在文件中的聲明,但沒有GHCI,在dreaded monomorphism restriction默認是開啓的):

import Debug.Trace 
main = print $ let x = (trace " Eval'd!" (* 2)) 3 in (x, x) 

此外,關於同一

let x = (trace " Eval'd!" 6) in (x,x) 

的行爲,所以它的真正意義的類型(類)歧義。

爲什麼它不共享的x所有用途的原因是因爲在GHC核心層面x,未優化,是一個真正的功能採取Num類型類詞典的說法。要分享它,它必須做足夠的分析才能看到類型是相同的。

爲什麼它不知道的原因基本上是GHCI被用於快速代碼實驗週轉,而不是創造良好的代碼,所以它在所有幾乎沒有優化,所以它幾乎是純粹的運氣是否檢測到這樣的事情與否。

(還有在GHCI字節碼設計,這意味着你不能啓用優化級別吧,就算你想,基本上它不支持「未裝箱元組」,其中GHC優化使用的outstanding hole很多。)

+0

我想我明白你的意思是'let x = 3' =>(x,x)::(Int,Integer)=>沒有類型錯誤。所以'x'和'x'不一定是相同的類型,因爲可以傳遞不同的'Num'字典。 有趣。 – obadz

+0

@obadz,進一步混淆事項,使某些類型系統擴展關閉讓泛化,迫使二者具有相同的類型(除非你給類型簽名)。儘管如此,我不知道這是否會改變GHCi。 – dfeuer

+0

@dfeuer哦,呵呵,這就是爲什麼把它放在一個文件中改變它,我應該解釋一下。 –