2012-11-24 29 views
6

由於我正在學習Haskell,我明白它是一種純粹的函數式語言。我無法理解爲什麼let -statements不違反純度。函數純度在Haskell中使用'let'

例如(在ghci中):

Prelude> let e = exp 1 
Prelude> e 
2.718281828459045 
Prelude> let e = 2 
Prelude> e 
2 

不是我的第二let聲明產生副作用?或者是第二個let聲明一個新的封閉?

+0

@jberryman 2)和3)已經在[我的答案]的下半部分中介紹過了(http://stackoverflow.com/questions/13545580/functional-purity-using-let-in-haskell/13545731#13545731 )下面是dbaupp的類似評論。 – AndrewC

回答

21

您的第二個lete創建了一個新的綁定,它會隱藏現有變量。它不會修改e。您可以輕鬆地用下面的檢查:

Prelude> let e = 1 
Prelude> let f() = "e is now " ++ show e 
Prelude> f() 
"e is now 1" 
Prelude> let e = 2 
Prelude> e 
2 
Prelude> f() 
"e is now 1" 
Prelude> 
16

let引入了一個不可改變的值的新的局部變量,它有更多的本地範圍比任何周圍的定義,因此,例如:

*Main> (let length = 2 in show length) ++ ' ':show (length "Hello") 
"2 5" 

這裏第一個length的值爲2,但其範圍在括號內。括號外,length意味着它一直的含義。沒有編輯任何東西,只是引入了更多的本地變量,它恰好與另一個範圍中的另一個名稱具有相同的名稱。讓我們通過省略括號,並使其儘量讓length編號和功能讓ghci的瘋狂:

*Main> let length = 2 in show length ++ ' ':show (length "Hello") 

<interactive>:1:14: 
    No instance for (Num ([Char] -> a0)) 
     arising from the literal `2' 
    Possible fix: add an instance declaration for (Num ([Char] -> a0)) 
    In the expression: 2 
    In an equation for `length': length = 2 
    In the expression: 
     let length = 2 in show length ++ ' ' : show (length "Hello") 

<interactive>:1:19: 
    No instance for (Show ([Char] -> a0)) 

     arising from a use of `show' 
    Possible fix: add an instance declaration for (Show ([Char] -> a0)) 
    In the first argument of `(++)', namely `show length' 
    In the expression: show length ++ ' ' : show (length "Hello") 
    In the expression: 
     let length = 2 in show length ++ ' ' : show (length "Hello") 

而且這裏是你的例子:

*Main> let e = exp 1 in show e ++ " " ++ let e = 2 in show e 
"2.718281828459045 2" 

我會加上括號強調的範圍:

*Main> let e = exp 1 in (show e ++ " " ++ (let e = 2 in (show e))) 
"2.718281828459045 2" 

第一個e被隱藏而不是編輯。參照透明度被保留,但這絕對是不好的做法,因爲它很難遵循。


現在偷偷的交互提示有點像在IO單子一個大do塊,所以讓我們看看:

testdo = do 
    let e = exp 1 
    print e 
    let e = 2 
    print e 

現在我不得不承認,看起來極像破但請記住,這看起來也是如此:

testWrite = do 
    writeFile "test.txt" "Hello Mum" 
    xs <- readFile "test.txt" 
    print xs 
    writeFile "test.txt" "Yo all" 
    xs <- readFile "test.txt" 
    print xs 

現在在什麼意義上我們獲得了參照透明度?明確指兩種不同的字符串。那麼這個do表示法實際上是什麼意思?它的語法糖爲

testWrite = writeFile "test.txt" "Hello Mum" 
     >> readFile "test.txt" 
     >>= (\xs -> print xs 
     >> writeFile "test.txt" "Yo all" 
     >> readFile "test.txt" 
     >>= (\xs -> print xs)) 

現在看起來像賦值的東西再一次變成了局部範圍。你大概很樂意做

increment :: [Int] -> [Int] 
increment = \x -> map (\x -> x+1) x 

這是做同樣的事情。


摘要
什麼似乎是分配僅僅是引進一個新的本地範圍。唷。如果你使用這個很多,你會非常不清楚你的代碼意味着什麼。

+6

請注意,該問題在do-notation上下文中使用'let',即'do {...;讓e = 1; ......而不是在......讓......在......的背景下。推理非常相似,但值得一提的是。 – huon

+0

@dbaupp好點。提及。謝謝。 – AndrewC