6

Haskellの学習に取り組んでいるので、それが純粋に関数型言語であることを理解しています。let-ステートメントが純度に違反しない理由を理解するのに苦労しています。

例(ghci):

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

私の2番目letのステートメントは副作用を引き起こしていませんか?それとも、2番目のletステートメントは新しい閉鎖ですか?

4

2 に答える 2

22

2つ目は、既存の変数をシャドウする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> 
于 2012-11-24T21:06:29.800 に答える
17

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"

1つ目eは、編集ではなく非表示になっています。参照透過性は維持されますが、従うのが難しいため、これは間違いなく悪い習慣です。


密かにインタラクティブプロンプトはdoIOモナドの1つの大きなブロックに少し似ているので、それを見てみましょう。

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

さて、どのような意味で参照透過性が得られたのでしょうか。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

これは同じことをしています。


まとめ
割り当てのように見えたのは、新しいローカルスコープの導入だけです。ふぅ。これを頻繁に使用すると、コードの意味が非常に不明確になります。

于 2012-11-24T21:21:10.607 に答える