2015-10-02 105 views
8

來自Haskell,我正在讀伊德里斯關於懶惰(非嚴格性)的故事。我觸輪近期發行說明和found code類似於以下伊德里斯真的「嚴格評估?」

myIf : (b : Bool) -> (t : Lazy a) -> (e : Lazy a) -> a 
myIf True t e = t 
myIf False t e = e 

我寫了一個簡單的階乘函數來測試它

myFact : Int -> Int 
myFact n = myIf (n == 1) 1 (n * myFact (n-1)) 

我跑它和它的工作!

> myFact 5 
120 : Int 

我決定通過改變myIf類型簽名破譯它

myIf : (b : Bool) -> a -> a -> a 

我重新加載idris REPL,跑myFact 5又懷孕了無限遞歸。令我驚訝的是,它仍然以同樣的方式工作!

idris能否確定何時應該避免嚴格?爲什麼這不是永久的遞歸?

我正在使用Idris 0.9.15,並且現在和鏈接的註釋之間沒有任何發行說明,請提及任何更改。

+0

我的REPL做同樣的事情。不過,如果我在REPL中用':x'調用'myFact',或編譯爲可執行文件,我似乎會得到一個無限循環。 –

+0

[Idris eager evaluation]的可能重複(http://stackoverflow.com/questions/23149532/idris-eager-evaluation) – Cactus

回答

15

的解釋是在這裏:http://docs.idris-lang.org/en/latest/faq/faq.html#evaluation-at-the-repl-doesn-t-behave-as-i-expect-what-s-going-on

編譯時間和運行時間評估的語義是不同的(必須如此,因爲在編譯時類型檢查器需要評估在未知值的存在下表達)以及REPL是使用編譯時的概念,爲了方便起見,並且因爲看看錶達式如何在類型檢查器中減少很有用。

但是,這裏還有更多。伊德里斯發現myIf是一個非常小的功能,並已決定將其內聯。所以編譯myFact時候,居然下了一個定義,看起來有點像:

myFact x = case x == 1 of 
       True => 1 
       False => x * myFact (x - 1) 

所以一般你可以寫像myIf控制結構,而不必擔心使事情Lazy,因爲伊德里斯將它們編成反正你想控制結構。同樣如此,例如&&||和短路。

+0

當它改變語義時,內聯優化是否正確? – luochen1990

+0

它不會改變語義。無論採用何種方式,您都可以得到相同的答案。 –

+0

但它改變了整體,這很難理解,特別是當用戶切換到一些類似的編碼風格時(例如,使代碼不再小到可以內聯的某些更改),期望不改變程序行爲,但程​​序可能不再工作.. – luochen1990