2012-11-24 6 views
6

ハスケルの学習に取り組んでいるので、私は純粋に機能的な言語だと理解しています。 letの文がなぜ純粋に違反しているのか理解できない。例えば(GHCiの中)Haskellで 'let'を使用する機能的純度

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

は、副作用を生じる私の第二letの文ではないでしょうか?または、2番目のletステートメントは新しいクロージャですか?

+0

@ jberryman 2)と3)はすでに[私の解答]の後半でカバーされていました(http://stackoverflow.com/questions/13545580/functional-purity-using-let-in-haskell/13545731#13545731 )以下のdbauppの同様のコメントに従います。 – AndrewC

答えて

21

letは、既存の変数をシャドウするeの新しいバインドを作成します。 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は編集されずに非表示になっています。参照透明度は保持されますが、それは難しいため間違いなく練習です。

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は明らかに2つの異なる文字列を参照しています。さて、これは実際には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 

これは同じことをしています。割り当てのように見えるもの


概要
は、新しいローカルスコープの単なる紹介です。 Phew。これをたくさん使うと、あなたのコードが何を意味するのかが非常に不明瞭になります。

+6

質問はdo表記のコンテキストで 'let'を使用することに注意してください。 e = 1とする。 ...} 'ではなく、' 'let ... in ... ''文脈の中で。推論はほとんど同じですが、言及する価値があります。 – huon

+0

@dbaupp良い点。言及した。ありがとう。 – AndrewC

関連する問題