Чому println вважається нечистою функцією?


10

Я читаю програму книги в масштабі, і сказано:

... в цьому випадку її побічним ефектом є друк до стандартного вихідного потоку.

і я не бачу, де побічний ефект, оскільки для того ж вводу println надрукує той самий вихід (я думаю)
ОНОВЛЕННЯ,
наприклад, у будь-який час, коли ми викликаємо:

println(5)

він надрукує 5 , я не бачу випадку, коли виклик println(5)надрукує значення, відмінне від 5 !!


якщо це відповість на ваше запитання, я
видалю

3
Відповіді на питання Що таке референтна прозорість? тут здаються актуальними.
Натан Х'юз


2
Ви плутали побічний ефект (не референційний прозорий) з детермінованим. printlnє детермінованою функцією, але щоб бути чистою, вона також повинна бути RT.
боб

2
Тому що він робить щось інше, ніж обчислювати результат і повертати його.
Сет Тису

Відповіді:


6

Ви можете сказати, чи має вираз побічний ефект, замінивши вираз на його результат. Якщо програма змінює значення , є побічний ефект. Наприклад,

println(5)

це інша програма для

()

Тобто побічний ефект - це будь-який спостережуваний ефект, який не кодується в результаті оцінки виразу. Тут результат є (), але в цьому значенні немає нічого, що кодує той факт, що 5 з'явилося десь на вашому екрані.


6
Насправді це не є хорошим визначенням "побічного ефекту" - побічний ефект може бути визначений як усе, що порушує прозорість. Що ви намагалися показати тут RT, але ваш приклад помиляється. Оскільки виконання чогось декількох разів повинно робити те саме, що неодноразово - Скоріше, val a = println("hello"); val b = (a, a)повинно бути те саме, що val b = (pritnln("hello"), println("hello")).
Луїс Мігель Мехіа Суарес

1
@ LuisMiguelMejíaSuárez, можливо, мій приклад не зрозумілий, але я не думаю, що це неправильно. Я по суті вказую на різницю між програмою println(5)та (). Або ви мали на увазі останнє речення?
joelb

Так, але вам це було не зрозуміло. Оскільки, як я вже сказав, проблема полягає не в тому, щоб викликати щось кілька разів, проблема полягає в тому, якщо заміна посилання на його визначення матиме такий вплив.
Луїс Мігель Мехіа Суарес

Я чітко не розумію вашого прикладу
aName

5
Я б сказав, що це неправильно, оскільки це цілком можливо, щоб щось мало побічний ефект і було безсильним, тому повторення цього не змінить ефекту. Наприклад, присвоєння змінної змінної; як можна розрізнити x = 1і x = 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
дякую, що відгукнулися, зрозуміло, що ваша функція нечиста, проте чому println (), як визначено в scala, не є чистим
aName

1
@aName Тому що, окрім значення, що повертається, ()він також мутує нелокальний стан у System.out.
Маріо Галич

Я думаю, що важливим фактом, якого не вистачає у цій відповіді, є те, що println додає новий текст у вхідний текст.
Федеріко S

4

Побічний ефект - у стані комп’ютера. Кожен раз, коли ви викликаєте println()стан пам'яті, змінюється, щоб відобразити задане значення терміналу. Або, загалом, стан стандартного вихідного потоку змінюється.


1
Частково правда, виконати будь-яку операцію буде стан лічильника інструкцій, таким чином, все є побічним ефектом. Визначення побічного ефекту походить від визначення референтної прозорості, яке багато людей визначають з точки зору модифікації загального стану, що змінюється.
Луїс Мігель Мехіа Суарес

2
таким чином, будь-яка функція, операція .... буде нечистою, оскільки вона зміниться, стан пам'яті процесора .....,
аНАМЕНЯ

2

На це запитання вже були приємні відповіді, але дозвольте додати свої два центи.

Якщо ви заглянете всередину printlnфункції по суті, це те саме, що java.lang.System.out.println()- тоді, коли ви посилаєтесь на стандартний printlnметод бібліотеки Scala під кришкою, він викликає метод printlnна PrintStreamекземплярі об'єкта, який оголошується як поле outв Systemкласі (або точніше outVarв Consoleоб'єкті), що змінює його внутрішній стан . Це можна розглядати як ще одне пояснення того, чому printlnфункція нечиста.

Сподіваюся, це допомагає!


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 ("ефект"), println ("ефект")) !!
aName

f(println("effect"), println("effect"))збирається надрукувати два рази з ефектом консолі, тоді val x = println("effect");f(x,x)як друкується один раз.
Дідак Монтеро

яке визначення функції f
aName
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.