printlnが不純な関数と見なされるのはなぜですか?


10

私は本のプログラミングをscalaで読んでいて、こう言われています:

...この場合、その副作用は標準出力ストリームへの出力です。

そして、私は副作用がどこにあるのかわかりません、なぜなら同じ入力に対して、printlnは同じ出力を出力するからです(私は思う)
UPDATE
を呼び出すときはいつでも:

println(5)

それが印刷されます5私が呼び出す場合表示されていない、println(5)5以外の値を出力しますが!


これがあなたの質問に答えたら、私の答えを削除しますsoftwareengineering.stackexchange.com/q/40297/271736
joelb

3
回答参照透明性とは何ですか?ここで関連があるようです。
Nathan Hughes


2
確定的と副作用(参照透過ではない)を混同しました。printlnは確定関数ですが、純粋であるためにはRTでなければなりません。
ボブ

2
結果を計算して返す以外のことを行うからです。
Seth Tisue

回答:


6

式をその結果で置き換えることにより、式に副作用があるかどうかを確認できます。プログラムの意味が変わった場合、副作用があります。例えば、

println(5)

別のプログラムです

()

つまり、副作用とは、式の評価結果にエンコードされていない、目に見える影響のことです。ここでは結果はですが()、5が画面のどこかに表示されているという事実をエンコードする値は何もありません。


6
実際、これは「副作用」の良い定義ではありません-副作用は、参照の透明性を損なうものとして定義される場合があります。ここで表示しようとしたのはRTですが、例は間違っています。何かを複数回実行すると、同じことを複数回実行する必要があるため- val a = println("hello"); val b = (a, a)と同じval b = (pritnln("hello"), println("hello"))です
Luis MiguelMejíaSuárez

1
@LuisMiguelMejíaSuárezおそらく私の例は明確ではありませんが、私はそれが間違っているとは思いません。私は基本的にプログラムprintln(5)との違いを指摘してい()ます。それとも最後の文ですか?
joelb

ええ、でもあなたはそれについてはっきりしていませんでした。私が言ったように、問題は何度も何かを呼び出すことではないので、問題は参照をその定義で置き換えることがその影響を持つかどうかです。
Luis MiguelMejíaSuárez

私はあなたの例を明確に理解していません
aName

5
何かが副作用を持ち、べき等であることは完全に可能であり、それを繰り返しても効果は変わりません。たとえば、可変変数への割り当て。どのように区別することができますx = 1x = 1; x = 1; x = 1
アレクセイロマノフ

5

次の例えを考えてみてください

var out: String = ""
def myprintln(s: String) = {
  out += s // this non-local mutation makes me impure
  ()
}

myprintln値を返すだけ()でなく、非ローカル変数outを副作用として変異させるため、ここでは不純です。今度outは、バニラがprintln変化するストリームであると想像してください。


1
回答していただきありがとうございます。関数が不純であることは明らかですが、なぜscalaで定義されているprintln()が純粋でないのですか
aName

1
@aNameは、値を返すだけ()でなく、ローカル以外の状態も変更するためSystem.outです。
マリオガリック

この回答に欠けている重要な事実は、printlnが入力に改行文字を追加することです。
フェデリコS

4

副作用はコンピューターの状態です。println()端末に与えられた値を表示するために、メモリの状態を呼び出すたびに変化します。より一般的には、標準出力ストリームの状態が変更されます。


1
部分的に真であり、任意の操作を実行すると、命令カウンターの状態になるため、何かが副作用になります。副作用の定義は、参照の透明性の定義から派生しています。これは、多くの人々が共有の可変状態への変更に関して定義します。
Luis

2
このように、関数、操作、...は変更されるため、メモリCPUの状態が
不正確に

2

この質問にはすでにいい答えが出ていますが、2セント追加してみましょう。

println関数の内部を見ると、基本的には同じです。つまり、内部でjava.lang.System.out.println()Scalaの標準ライブラリprintlnメソッドを呼び出すと、クラス(またはより正確にはオブジェクト)のフィールドとして宣言されprintlnているPrintStreamオブジェクトインスタンスのメソッドが呼び出され、内部状態が変更されます。 。これは、なぜ機能が不純であるかを説明するもう1つの理由と考えることができます。outSystemoutVarConsoleprintln

お役に立てれば!


1

これは、参照の透明性の概念に関係しています。プログラムを変更せずに式を評価結果で置き換えることができる場合、式は参照透過です。

式が参照的に透過的でない場合、副作用があると言います。

f(println("effect"), println("effect"))
// isn't really equivalent to!
val x = println("effect")
f(x, x)

ながら

import cats.effect.IO

def printlnIO(line: String): IO[Unit] = IO(println(line))

f(printlnIO("effect"), printlnIO("effect"))
// is equivalent to
val x = printlnIO("effect")
f(x, x)

ここでより詳細な説明を見つけることができます:https : //typelevel.org/blog/2017/05/02/io-monad-for-cats.html


f(x、x)がf(println( "effect")、println( "effect"))と異なる理由がわかりません!!
aName

f(println("effect"), println("effect"))コンソール「エフェクト」で2回印刷し、1回val x = println("effect");f(x,x)印刷します。
Didac Montero

関数fの定義は何
ですか
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.