wyjątki kompletnie bez sensu

1
Gworys napisał(a):
Patryk27 napisał(a):

side effect dotyczy sytuacji, w której funkcja np. zaczyna operować na czymś "z czapy" (czego nie otrzymała w argumentach) - przykład w jakimś pseudo-języku:

fn insert_z_czapy() {
  var db = service_container.get_service("database");
  db.insert("users", /* ... */);
}

fn insert_bez_efektow_ubocznych(db: Database) -> Database {
  db.insert("users", /* ... */)
}

Nie, jesteś w błędzie. "Side Efect" to po prostu zmiana stanu systemu.

Funkcja może mieć wewnętrznie mutowalny stan, a nadal być totalna i w 100% czysta. Według twojej definicji tak nie powinno być.

0
DisQ napisał(a):
Gworys napisał(a):
Patryk27 napisał(a):

side effect dotyczy sytuacji, w której funkcja np. zaczyna operować na czymś "z czapy" (czego nie otrzymała w argumentach) - przykład w jakimś pseudo-języku:

fn insert_z_czapy() {
  var db = service_container.get_service("database");
  db.insert("users", /* ... */);
}

fn insert_bez_efektow_ubocznych(db: Database) -> Database {
  db.insert("users", /* ... */)
}

Nie, jesteś w błędzie. "Side Efect" to po prostu zmiana stanu systemu.

Funkcja może mieć wewnętrznie mutowalny stan, a nadal być totalna i w 100% czysta. Według twojej definicji tak nie powinno być.

Według ogólnej definicji. Funkcja, która zmienia stan systemu, nie jest czysta.

0

Nie, jesteś w błędzie. "Side Efect" to po prostu zmiana stanu systemu.

Care to elaborate?

Artykuł na wiki oraz ten losowy wątek na Reddicie zdają się potwierdzać moją wersję:

Funkcja, która zmienia stan systemu, nie jest czysta.

Tak jak w poprzednim pytaniu - źródła (np. https://en.wikipedia.org/wiki/Pure_function) zdają się twierdzić inaczej.

4

Cały ten temat wygląda jak kłócenie się 20 architektów o jedną klasę w czasie gdy developer zdążył skończyć już cały projekt.

0
Gworys napisał(a):

Według ogólnej definicji. Funkcja, która zmienia stan systemu, nie jest czysta.

Ogólnie zgadzam się ze stwierdzeniem, że funkcja, która mutuje jakiś tam globalny stan nie jest czysta (chociaż tak jak napisałem wcześniej, wszystko zależy od scope'u tego stanu). Jednak sprowadzanie side-effectów tylko i wyłącznie do mutowania stanu jest lekkim ograniczeniem (bądź niedopowiedzeniem, zależy jak na to patrzeć).
Side effecty to też takie rzeczy jak wysyłanie żądań do innych serwisów, odpytywanie się bazy danych, odczyt pliku czy wysyłanie czegoś do konsoli. Można je sprowadzać do "zmiany globalnego stanu" lecz według mnie o wiele lepiej intencje efektów ubocznych oddaje stwierdzenie interakcji z jakimkolwiek zewnętrznym interfejsem (co niekoniecznie jest powiązane ze zmianą stanu).

Co do języków funkcyjnych to muszą dopuszczać pisanie funkcji z efektami ubocznymi, w innym przypadku Twój program nic by nie robił. Z sygnaturami też jest różnie, można napisać funkcję, która zwraca IO, a mimo wszystko wykonuje efekt uboczny (tu i teraz) ;) Nie mniej w idealnym świecie rzeczywiście powinno być tak, że sygnatura informuje o potencjalnym efekcie ubocznym.

0
Patryk27 napisał(a):

Nie, jesteś w błędzie. "Side Efect" to po prostu zmiana stanu systemu.

Care to elaborate?

Artykuł na wiki oraz ten losowy wątek na Reddicie zdają się potwierdzać moją wersję:

Funkcja, która zmienia stan systemu, nie jest czysta.

Tak jak w poprzednim pytaniu - źródła (np. https://en.wikipedia.org/wiki/Pure_function) zdają się twierdzić inaczej.

Opinia na Reddit tak za bardzo mnie nie obchodzi. Art z wiki zdaje się jedynie rozwija to co piszę.

Ludzie mają problem ze zrozumieniem tego, że efekt uboczny w informatyce to nie jest to samo, co znaczenie, którego używamy na co dzień. W programowaniu często celowe działanie nazywa się efektem ubocznym. Więc to nie jest defakto zrobienie czegoś nieprzewidywalnego, bo zmiana stanu systemu zazwyczaj jest czymś celowym i przewidywalnym, lecz z punktu widzenia funkcji jest efektem uboczny, ponieważ będzie to miało wpływ na operacje w przyszłości.

mr_jaro napisał(a):

Cały ten temat wygląda jak kłócenie się 20 architektów o jedną klasę w czasie gdy developer zdążył skończyć już cały projekt.

A za 2 miesiące, przyniósł wypowiedzenie :D

0
mr_jaro napisał(a):

Cały ten temat wygląda jak kłócenie się 20 architektów o jedną klasę w czasie gdy developer zdążył skończyć już cały projekt.

Ale ja Cię proszę bez science-fiction. Skąd niby architekt miałby wiedzieć, że jest coś takiego jak klasa?

0
DisQ napisał(a):

Ogólnie zgadzam się ze stwierdzeniem, że funkcja, która mutuje jakiś tam globalny stan nie jest czysta (chociaż tak jak napisałem wcześniej, wszystko zależy od scope'u tego stanu). Jednak sprowadzanie side-effectów tylko i wyłącznie do mutowania stanu jest lekkim ograniczeniem (bądź niedopowiedzeniem, zależy jak na to patrzeć).
Side effecty to też takie rzeczy jak wysyłanie żądań do innych serwisów, odpytywanie się bazy danych, odczyt pliku czy wysyłanie czegoś do konsoli. Można je sprowadzać do "zmiany globalnego stanu" lecz według mnie o wiele lepiej intencje efektów ubocznych oddaje stwierdzenie interakcji z jakimkolwiek zewnętrznym interfejsem (co niekoniecznie jest powiązane ze zmianą stanu).

To przedstaw mi jakieś polecenie (znane również jako modyfikator) bez efektów ubocznych...

Co do języków funkcyjnych to muszą dopuszczać pisanie funkcji z efektami ubocznymi, w innym przypadku Twój program nic by nie robił. Z sygnaturami też jest różnie, można napisać funkcję, która zwraca IO, a mimo wszystko wykonuje efekt uboczny (tu i teraz) ;) Nie mniej w idealnym świecie rzeczywiście powinno być tak, że sygnatura informuje o potencjalnym efekcie ubocznym.

No, tak jak mówiłem nie ma języka funkcyjnego bez efektów ubocznych.

1

Czepiając się bardzo - oczywioście funkcje czysto czyste prawie nie istnieją, bo o ile są ewaluowane to mamy takie obserwowalne efekty jak zużcie prądu, podgrzenie procka, zmiany liczników CPU, być może jakieś alokacje w pamięci.

Natomiast praktyczna denicja jest taka, że funkcja czysta to taka, gdzie wynik zalezy tylko od argumentów. I patrzymy na rzeczy istotne z punktu widzenia logiki systemu. Podgrzewanie procka zwykle nie jest istotne. Mówi się wtedy, że nie ma efektów ubocznych.

Przy czym w Haskellu czy Scali funkcje, postaci f :: x -> IO () , def f [X] (x:X) : IO[Unit] są w istocie czyste. Nie mają efektów ubocznych.
Może w środku jest kod pisania na ekran i pobierania z bazy danych, ale funkcja jest zupełnie czysta (o ile oczywiście programista na siłe nie oszukał, co w Scali zrobić łatwo, a w Haskellu trudno).
To tylko pozorny paradoks - tak działa monada IO. Łatwo się o tej czystości przekonać jeśli wynik takiej funkcj przepuścić przez dziwaczną funkcję typu: whatever :: a -> ().

f ::  IO ()
f = do  
    putStrLn "Write something"  
    x <- getLine  
    putStrLn ("Input was " ++ x)  


whatever :: a -> () 
whatever _ = ()

main = f -- wyświetli się co trzeba
-- main = return $ whatever  f  -- ciekawostka

Efekty są po prostu zakodowane w rezultacie (IO), a sama funkcja nic ubocznego nie robi.
Dzieki temu o wiele łatwiej jest analizować i testować programy.

W językach imperatywnych można przyjąc, że każda funkcja ma ukryte dodatkowe argumenty i rezultaty czyli zamiast f :: a ->b mamy f:: (a,STATE, IO) -> (b, STATE, IO). Gdzie STATE to jakiś globalny widzialny stan, a IO to wykonywane operacje wejścia/wyjścia. Nie dość ,że to od razu o wiele więcej potencjalnych powiązań do analizy to jeszcze utrudnia testowanie. Ale najgorsze jest to, że nie ma możliwości (tak jak w językach funkcyjnych) olania, cofnięcia zmian na IO. Po wykonaniu funkcji mleko się rozlało, baza dostała INSERTA a STATE sie zmienił i nie ma żadnego prostego rollbacka.

Z jednym się zgadzam: programownaie funkcyjne to właśnie programowanie z efektami ubocznymi. Z kontrolowanymi efektami ubocznymi. Przy czym są one kontrolowalne tak długo jak nasze funkcje efektów ubocznych nie mają.

0

Z jednym się zgadzam: programownaie funkcyjne to właśnie programowanie z efektami ubocznymi. Z kontrolowanymi efektami ubocznymi. Przy czym są one kontrolowalne tak długo jak nasze funkcje efektów ubocznych nie mają.

Czyli "Funkcja czysta" Defakto nie musi wykonywać operacji, które są pozbawione efektów ubocznych.

Mnie nie obchodzi to, co ta funkcja ma, tylko jaki jest efekt jej działania.

Jeśli w wyniku jej działania zostaje zmodyfikowany jakiś stan programu poza swoim lokalnym środowiskiem, to znaczy, że efekt uboczny wystąpił.

Czepiając się bardzo - oczywioście funkcje czysto czyste prawie nie istnieją, bo o ile są ewaluowane to mamy takie obserwowalne efekty jak zużcie prądu, podgrzenie procka, zmiany liczników CPU, być może jakieś alokacje w pamięci.

Uważam że mylisz definicje efektów ubocznych znanymi z programowania z czymś zupełnie innym.

link

0

@jarekr000000:

Ciekawe co na to powiesz strona 152:
https://alvinalexander.com/downloads/fpsimplified-free-preview.pdf

“The IO monad does not make a function pure. It just makes it obvious
that it’s impure.”

1
Gworys napisał(a):

@jarekr000000:

Ciekawe co na to powiesz strona 152:
https://alvinalexander.com/downloads/fpsimplified-free-preview.pdf

“The IO monad does not make a function pure. It just makes it obvious
that it’s impure.”

Poczytaj sobie https://www.reddit.com/r/scala/comments/8ygjcq/can_someone_explain_to_me_the_benefits_of_io/. Lektura jest długa ale warta przeczytania

0
Gworys napisał(a):

@jarekr000000:

Ciekawe co na to powiesz strona 152:
https://alvinalexander.com/downloads/fpsimplified-free-preview.pdf

“The IO monad does not make a function pure. It just makes it obvious
that it’s impure.”

Ten tekst to Oderskiego i nie znam całego kontekstu, ale wprost brzmi jak herezja.
A co do Alvina Alexandra - nie znam gościa ale straszne herezje pisze. Może nie przeczytał:
https://www.haskell.org/tutorial/io.html

Np. tu:
https://alvinalexander.com/scala/fp-book/pure-functions-and-io-input-output

The Haskell compiler is free to optimize any code that does not return something of type IO. This topic really requires a long discussion, but in short, the Haskell compiler is free to re-order all non-IO code in order to optimize it. Because ...

Bzdura. Może i ghc czy inny kompilator ma jakieś specjalne reguły dotyczące IO, ale wątpie. Duży sens monady IO w haskellu to właśnie to jak radzić sobie z IO z założeniem, że kompilator jak najbardziej zoptymalizuje kod i ma gdzieś, że czytasz z pliku.
Można sobie napisać własną monadę IO - DupIO i też będzie działać , o ile napisze się podobnie jak oryginalne.

Może gość nie pisał za bardzo w Haskellu, bo faktycznie w Scali całe to IO ma dużo mniejszy sens ( przynajmniej do niedawna tak mi się wydawało).

EDIT:
przykład jak IO działa i dlaczego jest czyste... z wykorzystaniem prawdziwej nieczystości:

iimport System.IO.Unsafe
f :: Int -> IO (String)
f a = do
    return $! unsafePerformIO $ putStrLn "we are computing...."
    putStrLn "Write something"
    x <- getLine
    return $ x ++ show a


main = x >> x >>= putStrLn
  where
    x = id $! f 5

(pod ghc)
W tym kodzie f jest nieczyste. Ale nie dlatego, ze zwraca IO tylko dlatego, że wykonuje unsafePerformIO -> po to żeby na siłę rzucić na ekran, że coś robimy. (sprawdzamy ile razy wchodzimy do funkcji -taki smutny debug).
W main Funkcja jest raz wykonywana i jej wynik jest podstawiony do x. Wynik w postaci IO.
Mimo, że funkcja jest ewaluowana tylko raz - co widać, bo napis brzydko sie bawimy pojawi się raz to program ładnie - dwa razy pyuta się "Write Something".

W ogóle polecam odpalenie, bo można zobaczyć kiedy ten debug się wyświetli - jest to ciekawe i istotne dla zrozumienia IO.
Co pokazuje, że oryginalne f:

f :: Int -> IO (String)
f a = do
    putStrLn "Write something"
    x <- getLine
     return $ x ++ show a

(f bez nieczystego debug kodu).
zachowuje przynajmniej referential transparency.

0
jarekr000000 napisał(a):
Gworys napisał(a):

@jarekr000000:

Ciekawe co na to powiesz strona 152:
https://alvinalexander.com/downloads/fpsimplified-free-preview.pdf

“The IO monad does not make a function pure. It just makes it obvious
that it’s impure.”

Ten tekst to Oderskiego i nie znam całego kontekstu, ale wprost brzmi jak herezja.
A co do Alvina Alexandra - nie znam gościa ale straszne herezje pisze. Może nie przeczytał:
https://www.haskell.org/tutorial/io.html

Np. tu:
https://alvinalexander.com/scala/fp-book/pure-functions-and-io-input-output

The Haskell compiler is free to optimize any code that does not return something of type IO. This topic really requires a long discussion, but in short, the Haskell compiler is free to re-order all non-IO code in order to optimize it. Because ...

Bzdura. Może i ghc czy inny kompilator ma jakieś specjalne reguły dotyczące IO, ale wątpie. Duży sens monady IO w haskellu to właśnie to jak radzić sobie z IO z założeniem, że kompilator jak najbardziej zoptymalizuje kod i ma gdzieś, że czytasz z pliku.
Można sobie napisać własną monadę IO - DupIO i też będzie działać , o ile napisze się podobnie jak oryginalne.

Może gość nie pisał za bardzo w Haskellu, bo faktycznie w Scali całe to IO ma dużo mniejszy sens ( przynajmniej do niedawna tak mi się wydawało).

The I/O system in Haskell is purely functional, yet has all of the expressive power found in conventional programming languages. In imperative languages, programs proceed via actions which examine and modify the current state of the world. Typical actions include reading and setting global variables, writing files, reading input, and opening windows. Such actions are also a part of Haskell but are cleanly separated from the purely functional core of the language.

A jakie ma to znaczenie dla programisty wysoko poziomowego. Zapis do DB, czy pliku to zawsze efekt uboczny.

Tak patrząc na podsumowanie, to gdzie są te herezje.?

Summary
As I showed in this lesson, when you need to write I/O code in functional programming languages, the solution is to violate the “Only Write Pure Functions” rule. The general idea is that you write as much of your application as possible in an FP style, and then handle the UI, Database I/O, Web Service I/O, and File I/O in the best way possible for your current programming language and tools.

I also showed that wrapping your I/O functions in an IO type doesn’t make a function pure, but it is a great way to add something to your function’s type signature to let every know, “This function deals with I/O.” When a function returns a type like IO[String] you can be very sure that it reached into the outside world to get that String, and when it returns IO[Unit], you can be sure that it wrote something to the outside world.

0
Gworys napisał(a):

A jakie ma to znaczenie dla programisty wysoko poziomowego. Zapis do DB, czy pliku to zawsze efekt uboczny.

Co dokładnie tam jest herezją to, że interakcja ze światem zewnętrznym to efekt uboczny.?

Ma to znaczneie podstawowe. Na ile i jak bezpiecznie mozesz refaktorować kod i jak łatwo możesz testować.
Fizyczny Zapis do DB, czy pliku to zawsze efekt uboczny. oczywiście.
Ale funkcja, która tą logikę opisuje efektów ubocznych może nie mieć i jest to wygodne (bo np. możesz rezultat przechować w zmiennej).

Oczywiście, gdzieś w końcu są nieczyste funkcje w bibliotece bazowej, które coś robią nieczysto - ważne, że nie mamy ich w naszej logice.

0
jarekr000000 napisał(a):
Gworys napisał(a):

A jakie ma to znaczenie dla programisty wysoko poziomowego. Zapis do DB, czy pliku to zawsze efekt uboczny.

Co dokładnie tam jest herezją to, że interakcja ze światem zewnętrznym to efekt uboczny.?

Ma to znaczneie podstawowe. Na ile i jak bezpiecznie mozesz refaktorować kod i jak łatwo możesz testować.
Fizyczny Zapis do DB, czy pliku to zawsze efekt uboczny. oczywiście.
Ale funkcja, która tą logikę opisuje efektów ubocznych może nie mieć i jest to wygodne (bo np. możesz rezultat przechować w zmiennej).

Oczywiście, gdzieś w końcu są nieczyste funkcje w bibliotece bazowej, które coś robią nieczysto - ważne, że nie mamy ich w naszej logice.

No właśnie mnie się bardzo nie podoba określenie, "Czysta funkcja to funkcja bez efektów ubocznych" ponieważ wykonanie tej funkcji może powodować efekty uboczne, te efekty są po prostu wyizolowane, ale funkcja dalej nimi w pewnym stopniu zarządza.

0
Gworys napisał(a):

No właśnie mnie się bardzo nie podoba określenie, "Czysta funkcja to funkcja bez efektów ubocznych" ponieważ wykonanie tej funkcji może powodować efekty uboczne, te efekty są po prostu wyizolowane, ale funkcja dalej nimi w pewnym stopniu zarządza.

Otóż nie, nie powoduje (o ile jest czysta).
Przez analogię
Czy funkcja, która zwraca ciąg tekstowy:

fun nibyFormat()  = "format  c: /fs:NTFS /p:1"

Faktycznie formatuje dysk?

Otóż nie. Nieważne jak groźnie to wygląda. To sformatowanie nastąpi tylko jak wynik tej funkcji wkleisz do command line w windows.

W haskellu jeśli funkcja main zwróci IO () to runtime haskellowy weźmie to i wykona. Ale to dotyczy tylko tego jednego miejsca.
W pozostałych czy te IO (X) bedzie wykonane, czy nie ...zależy od programisty.
Generalnie programuje się tak, żeby to całe IO zwrócić aż do main, ale można nieczysto wykonać wcześniej - wtedy nieczysta nie będzie ta funkcja , która zwraca IO, tylko własnie ta która tego IO nie zwraca (!!!) (a połyka...).

Zresztą jest taka w standardzie i nazywa sie
unsafePerformIO - jest nieczysta według wszelakich definicji. I właśnie nie zwraca IO !
http://hackage.haskell.org/package/base-4.12.0.0/docs/System-IO-Unsafe.html

0

Czy funkcja, która zwraca ciąg tekstowy:

fun nibyFormat() = "format c: /fs:NTFS /p:1"
Faktycznie formatuje dysk?

Otóż nie. Nieważne jak groźnie to wygląda. To sformatowanie nastąpi tylko jak wynik tej funkcji wkleisz do command line w windows.

W haskellu jeśli funkcja main zwróci IO (X) to runtime haskellowy weźmie to i wykona.

No, ale jeśli runtime haskellowy weźmie to i wykona to efekt uboczny istnieje. Nie rozumiem, może mi to przejdzie jak popiszę więcej w Haskell'u albo się pogłębi. :)

Zawsze uważałem, że IO to taki bardziej sugar, który wskazuje na efekt uboczny.

1 użytkowników online, w tym zalogowanych: 0, gości: 1