Dlaczego println jest uważany za nieczystą funkcję?

10

Czytam programowanie książek w scali i jest powiedziane:

... w tym przypadku jego efektem ubocznym jest drukowanie do standardowego strumienia wyjściowego.

i nie widzę, gdzie jest efekt uboczny, ponieważ dla tego samego wejścia println wydrukuje to samo wyjście (myślę)
UPDATE
na przykład za każdym razem, gdy wywołujemy:

println(5)

wypisze 5 , nie widzę przypadku, w którym wywołanie println(5)wypisze wartość inną niż 5 !!

imię
źródło
jeśli to odpowie na twoje pytanie, usunę moją odpowiedź softwareengineering.stackexchange.com/q/40297/271736
joelb
3
Odpowiedzi na temat Co to jest przejrzystość referencyjna? wydają się tutaj istotne.
Nathan Hughes
1
powiązane (lol) stackoverflow.com/q/4865616/5986907
joelb
2
Pomieszałeś efekt uboczny (nie referencyjny transparentny) z deterministycznym. printlnjest funkcją deterministyczną, ale aby być czystą, musi być również RT.
Bob
2
Ponieważ robi coś innego niż obliczenie wyniku i zwrócenie go.
Seth Tisue

Odpowiedzi:

6

Możesz stwierdzić, czy wyrażenie ma efekt uboczny, zastępując wyrażenie jego wynikiem. Jeśli program zmienia znaczenie , występuje efekt uboczny. Na przykład,

println(5)

jest innym programem niż

()

Oznacza to, że efektem ubocznym jest każdy możliwy do zaobserwowania efekt, który nie jest zakodowany w wyniku oceny wyrażenia. Oto wynik (), ale w tej wartości nie ma nic, co by kodowało fakt, że 5 pojawiło się gdzieś na ekranie.

joelb
źródło
6
W rzeczywistości nie jest to dobra definicja „efektu ubocznego” - Efekt uboczny można zdefiniować jako wszystko, co narusza przejrzystość referencyjną. Próbowałeś tu pokazać RT, ale twój przykład jest zły. Ponieważ wykonywanie czegoś wiele razy powinno robić to samo wiele razy - Raczej val a = println("hello"); val b = (a, a)powinno być takie samo jak val b = (pritnln("hello"), println("hello")).
Luis Miguel Mejía Suárez
1
@ LuisMiguelMejíaSuárez, być może mój przykład nie jest jasny, ale nie sądzę, że jest źle. Zasadniczo zwracam uwagę na różnicę między programem println(5)a (). A może miałeś na myśli ostatnie zdanie?
joelb
Tak, ale nie było to jasne. Ponieważ, jak powiedziałem, problemem nie jest wywoływanie czegoś wiele razy, problem polega na tym, że zastąpienie odwołania jego definicją miałoby taki wpływ.
Luis Miguel Mejía Suárez
Nie rozumiem
dokładnie
5
Powiedziałbym, że to źle, ponieważ jest całkowicie możliwe, że coś ma efekt uboczny i jest idempotentny, więc powtarzanie go nie zmienia efektu. Np. Przypisanie do zmiennej zmiennej; jak możesz odróżnić x = 1i x = 1; x = 1; x = 1?
Aleksiej Romanow
5

Rozważ następującą analogię

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

Tutaj myprintlnjest nieczysta, bo obok powrocie wartość ()it także mutuje zmienne non-local outjako efekt uboczny. Teraz wyobraź sobie, outże jest printlnmutacją waniliową .

Mario Galic
źródło
1
dziękuję za odpowiedź, jasne jest, że twoja funkcja jest nieczysta, jednak dlaczego println (), jak zdefiniowano w scala, nie jest czyste
aName
1
@ aName Ponieważ oprócz zwracanej wartości ()mutuje również stan nielokalny w System.out.
Mario Galic,
Myślę, że kluczowym faktem, którego brakuje w tej odpowiedzi, jest to, że println dodaje znak nowej linii do danych wejściowych.
Federico S
4

Efektem ubocznym jest stan komputera. Każdorazowe wywołanie println()zmiany stanu pamięci w celu wyświetlenia danej wartości na terminalu. Lub bardziej ogólnie, stan standardowego strumienia wyjściowego ulega zmianie.

Uczeń kodowy
źródło
1
Częściowo prawda, wykonanie dowolnej operacji spowoduje stan licznika instrukcji, więc wszystko jest efektem ubocznym. Definicja efektu ubocznego wywodzi się z definicji przejrzystości referencyjnej, którą wiele osób definiuje w kategoriach modyfikacji wspólnego stanu zmiennego.
Luis Miguel Mejía Suárez
2
w ten sposób każda funkcja, działanie .... będzie nieczyste, ponieważ się zmieni, stan pamięci procesora .....,
aName
2

Odpowiedzi na to pytanie zostały już udzielone, ale dodam dwa grosze.

Jeśli zajrzysz do printlnfunkcji, zasadniczo jest to to samo cojava.lang.System.out.println() - więc kiedy wywołujesz standardową printlnmetodę biblioteczną Scali pod maską, wywołuje ona metodę printlnna PrintStreaminstancji obiektu, która jest zadeklarowana jako pole outw Systemklasie (a ściślej outVarw Consoleobiekcie), co zmienia jej stan wewnętrzny . Można to uznać za kolejne wyjaśnienie, dlaczego printlnfunkcja nieczysta jest.

Mam nadzieję że to pomoże!

Iwan Kurczenko
źródło
1

Ma to związek z koncepcją przejrzystości referencyjnej . Wyrażenie jest referencyjnie przezroczyste, jeśli możesz go zastąpić ocenionym wynikiem bez zmiany programu .

Kiedy wyrażenie nie jest względnie przejrzyste, mówimy, że ma skutki uboczne .

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

podczas

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)

Bardziej szczegółowe wyjaśnienie można znaleźć tutaj: https://typelevel.org/blog/2017/05/02/io-monad-for-cats.html

Didac Montero
źródło
Nie rozumiem, dlaczego f (x, x) różni się od f (println („efekt”), println („efekt”)) !!
aName
f(println("effect"), println("effect"))wydrukuje dwa razy w „efekcie” konsoli, podczas gdy val x = println("effect");f(x,x)wydrukuje raz.
Didac Montero
jaka jest definicja funkcji f
aName