Почему println считается нечистой функцией?

10

Я читаю книгу программирования в Scala, и там сказано:

... в этом случае его побочным эффектом является печать в стандартный поток вывода.

и я не вижу, где находится побочный эффект, так как для того же ввода println будет печатать один и тот же вывод (я думаю)
UPDATE,
например, каждый раз, когда мы вызываем:

println(5)

он напечатает 5 , я не вижу случая, когда вызов println(5)будет печатать значение, отличное от 5 !!

имя
источник
если это ответит на ваш вопрос, я удалю свой ответ softwareengineering.stackexchange.com/q/40297/271736
joelb
3
Ответы в разделе Что такое ссылочная прозрачность? кажется актуальным здесь.
Натан Хьюз
1
related (lol) stackoverflow.com/q/4865616/5986907
joelb
2
Вы перепутали побочный эффект (не ссылочный прозрачный) с детерминированным. printlnявляется детерминированной функцией, но для того, чтобы быть чистым, она также должна быть RT.
боб
2
Потому что он делает что-то другое, чем вычисляет результат и возвращает его.
Сет Тису

Ответы:

6

Вы можете сказать, имеет ли выражение побочный эффект, заменив выражение его результатом. Если программа меняет смысл , есть побочный эффект. Например,

println(5)

это другая программа

()

То есть побочный эффект - это любой наблюдаемый эффект, который не закодирован в результате вычисления выражения. Вот результат (), но в этом значении нет ничего, что кодировало бы тот факт, что 5 теперь появилось где-то на вашем экране.

joelb
источник
6
На самом деле это не очень хорошее определение «побочного эффекта». Побочный эффект может быть определен как что-либо, что нарушает ссылочную прозрачность. То, что вы пытались показать здесь, было RT, но ваш пример неверен. Поскольку выполнение чего-либо несколько раз должно повторять одно и то же несколько раз - скорее, val a = println("hello"); val b = (a, a)должно быть то же самое, что и val b = (pritnln("hello"), println("hello")).
Луис Мигель Мехия Суарес
1
@ LuisMiguelMejíaSuárez возможно мой пример не ясен, но я не думаю, что это неправильно. Я по сути указываю на разницу между программой println(5)и (). Или вы имели в виду последнее предложение?
Джоэлб
Да, но ты не был уверен в этом. Поскольку, как я уже сказал, проблема заключается не в том, чтобы вызывать что-то несколько раз, проблема заключается в том, что замена ссылки на ее определение будет иметь такое влияние.
Луис Мигель Мехия Суарес
Я не ясно понять ваш пример
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 добавляет символ новой строки к вводу.
Федерико С.
4

Побочный эффект в состоянии компьютера. Каждый раз, когда вы вызываете, println()состояние памяти изменяется, чтобы отобразить заданное значение для терминала. Или, в более общем случае, состояние стандартного выходного потока изменяется.

Код-Apprentice
источник
1
Частично верно, выполнение любой операции будет в состоянии счетчика команд, таким образом, что-либо является побочным эффектом. Определение побочного эффекта происходит от определения ссылочной прозрачности, которое многие люди определяют в терминах изменений общего изменяемого состояния.
Луис Мигель Мехия Суарес
2
таким образом, любая функция, операция .... будет нечистой, так как она изменится, состояние памяти процессора .....,
aName
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 («эффект»)) !!
Имя
f(println("effect"), println("effect"))собирается печатать два раза в консоли «эффект», в то время val x = println("effect");f(x,x)как собирается печатать один раз.
Didac Montero
каково определение функции f
aName