2012-11-24 4 views
6

하스켈을 배우기 위해 순수 함수 언어라는 것을 알고 있습니다. let - 명언이 순결을 위반하지 않는 이유를 이해하는 데 문제가 있습니다. 예를 들어 (ghci에서)Haskell에서 'let'을 사용하는 기능적 순도

:

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

두 번째 let은 기존 변수를 음영 처리하는 에 대한 새 바인딩을 만듭니다. e을 수정하지 않습니다. 당신은 쉽게 다음과 같이이 문제를 확인할 수 있습니다

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

:

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는 하나의 불변 값을 가진 새로운 지역 변수를 소개하고는, 어떤 주변의 정의보다 더 많은 지역의 범위가 예를 들어, 그래서 여기서 첫 번째 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

숨겨보다는 편집됩니다. 참조 투명성은 유지되지만, 따라하기가 어렵 기 때문에 분명히 나쁜 습관입니다.

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

가 지금은 그 파괴처럼 엄청 많이 보인다 인정해야 :


지금 비밀리에 대화 형 프롬프트는 IO 모나드에서 하나 개의 큰 do 블록 같은 비트, 그래서 그 살펴 보자입니다 참조 투명성을 보장하지만이 방법도 그렇게 보이는 것은 염두에 두십시오.

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

이제 어떤 의미에서 참조 투명성을 얻었습니까? 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 표기법 문맥에서'let'을 사용합니다. 즉 do {...; let e = 1; ...} '문맥보다는 ...'문맥에서. 추론은 거의 동일하지만 언급 할만한 가치가 있습니다. – huon

+0

@dbaupp 좋은 지적. 말하는. 감사. – AndrewC