関数と呼ばれる参照透過性は、引数の値を見るだけでその関数を適用した結果を決定できることを示します。Python、Scheme、Pascal、Cなどの任意のプログラミング言語で参照透過関数を作成できます。
一方、ほとんどの言語では、参照を意識しない透過関数を作成することもできます。たとえば、次のPython関数:
counter = 0
def foo(x):
global counter
counter += 1
return x + counter
参照的に透過的ではなく、実際に呼び出す
foo(x) + foo(x)
そして
2 * foo(x)
任意の引数に対して異なる値を生成しますx
。これは、関数がグローバル変数を使用および変更するためです。したがって、各呼び出しの結果は、関数の引数だけでなく、この変化する状態に依存します。
純粋に関数型の言語であるHaskellは、純粋な関数が適用され、常に参照的に透過的である式評価を、参照的に透過的ではないアクション実行(特別な値の処理)から厳密に分離します。つまり、同じアクションを実行するたびに異なる結果。
そのため、Haskell関数については
f :: Int -> Int
および任意の整数x
、それは常に真です
2 * (f x) == (f x) + (f x)
アクションの例は、ライブラリ関数の結果ですgetLine
:
getLine :: IO String
式の評価の結果、この関数(実際は定数)はまずtypeの純粋な値を生成しますIO String
。このタイプの値は他の値と同じです。値を渡したり、データ構造に入れたり、特別な関数を使用して構成したりできます。たとえば、次のようなアクションのリストを作成できます。
[getLine, getLine] :: [IO String]
アクションは、Haskellランタイムに次のように記述して実行するよう指示できるという点で特別です。
main = <some action>
この場合、Haskellプログラムが開始されると、ランタイムはバインドされたアクションmain
を実行して実行し、場合によっては副作用を引き起こします。したがって、同じアクションを2回実行すると、ランタイムが入力として取得するものに応じて異なる結果が生成される可能性があるため、アクションの実行は参照的に透過的ではありません。
Haskellの型システムのおかげで、別の型が期待されるコンテキストではアクションを使用できません。逆の場合も同様です。したがって、文字列の長さを検索する場合は、次のlength
関数を使用できます。
length "Hello"
5を返します。ただし、端末から読み取った文字列の長さを検索する場合は、書き込むことができません。
length (getLine)
タイプエラーが発生するためです。length
タイプリストの入力(および文字列は実際にはリスト)を期待しますが、タイプgetLine
の値IO String
(アクション)です。このようにして、型システムは、getLine
(実行がコア言語の外部で実行され、非参照的に透過的である可能性がある)などのアクション値がtypeの非アクション値内に隠れないようにしますInt
。
編集
exiztの質問に答えるために、コンソールから行を読み取り、その長さを出力する小さなHaskellプログラムを次に示します。
main :: IO () -- The main program is an action of type IO ()
main = do
line <- getLine
putStrLn (show (length line))
メインアクションは、順次実行される2つのサブアクションで構成されます。
getline
タイプのIO String
、
- 2つ目は、引数
putStrLn
の型の関数を評価することにより構築されString -> IO ()
ます。
より正確には、2番目のアクションは
line
最初のアクションによって読み取られた値へのバインド、
- 純関数を評価し
length
(整数として長さを計算する)、次にshow
(整数を文字列に変換する)、
putStrLn
の結果に関数を適用してアクションを構築しshow
ます。
この時点で、2番目のアクションを実行できます。「Hello」と入力した場合、「5」が出力されます。
<-
表記法を使用してアクションから値を取得する場合、その値は別のアクション内でのみ使用できることに注意してください。たとえば、次のように書くことはできません。
main = do
line <- getLine
show (length line) -- Error:
-- Expected type: IO ()
-- Actual type: String
ので、show (length line)
型を持つString
DO表記は、(アクションことを必要とするのに対し、getLine
タイプのはIO String
)別のアクションが続く(例えばputStrLn (show (length line))
タイプのIO ()
)。
編集2
JörgW Mittagの参照の透明性の定義は、私のものよりも一般的です(彼の答えを支持しました)。質問の例は関数の戻り値に焦点を当てているため、制限された定義を使用し、この側面を説明したかったためです。ただし、RTは一般に、プログラム全体の意味を指します。これには、グローバルな状態の変更や、式の評価によって引き起こされる環境(IO)との相互作用が含まれます。したがって、正しい一般的な定義については、その答えを参照する必要があります。