Czy czyste funkcje mogą przyjmować argumenty tylko przez wartość?

Odpowiedz Nowy wątek
2019-08-16 17:04
2

Przejrzałem internet i obecnie mam następującą definicję czystej funkcji (niezależnie od języka programowania):

  1. Zwracana wartość (jeśli ją ma?) zależy wyłącznie od jej argumentów. — Mówiąc kolokwialnie, nie czyta z obiektu dostępnego z zewnątrz. [wykreślone, bo błędne]
  2. Nie posiada skutków ubocznych (side effects). — Mówiąc kolokwialnie, nie pisze do obiektu dostępnego z zewnątrz. [wykreślone, bo być może błędne]

Jednak zastanawia mnie pewna rzecz: z uwagi na punkt nr 1 wszystkie funkcje, które przyjmują argumenty przez referencję, nie będą czyste.

Dedukując, za czystą można uważać jedynie funkcję przyjmującą argumenty przez wartość.

Dobrze myślę?


UPDATE: Przykład w języku JavaScript:

// Funkcja, która moim zdaniem jest czysta według powyższej definicji
//    - liczby przekazywane są przez wartość *
function(someNumber) {
    return someNumber + 1;
}

// Funkcja, która moim zdaniem nie jest czysta według powyższej definicji
//    - obiekty przekazywane są przez referencję *
function(someObject) {
    return someObject.someProperty;
}

UPDATE2: * W języku JavaScript funkcje generalnie uznawane są za takie, które przyjmują argumenty przez wartość (np. według tej dokumentacji MDN), ale najpewniej nie o takie rozróżnienie mi chodzi. MDN podaje, że "object references are values". Dlatego też pominąłem to rozróżnienie wyżej.


edytowany 13x, ostatnio: Silv, 2019-08-17 00:50

Pozostało 580 znaków

2019-08-16 18:35
4

porownywanie referencji nie ma sensu przy ocenianiu czy funkcja jest czysta, powinno sie porownywac stan obiektow wskazywanych przez te referencje... przynajmniej w takiej javie, c++, c#

tak! :) nawet nie widząc twojego posta, zedytowałem swojego dodając do niego właśnie myśl, że jak zmienimy obiekt poza funkcją, to w rezultacie jest to inny już obiekt (w środku), więc funkcja dostaje inny argument (nawet jeśli o tej samej referencji) więc ma prawo się zachować inaczej... - LukeJL 2019-08-16 18:43
Też próbuje to wytłumaczyć, ale niektórzy się uparli... - scibi92 2019-08-16 18:43
@scibi92: kto się uparł? ;) Jeśli chodzi o mnie, ja się z @katelx nie wiem, czy zgadzam. Natomiast zgadzam się z poprzednim postem @LukeJL. Między innymi dzięki temu postowi doszedłem obecnie do przekonania, że funkcja może być czysta, jeśli modyfikuje te same "obiekty" w taki sam sposób. Nadal jednak porównywałbym referencje, nie obiekty pod spodem. Ale nie wykluczam i takiego podejścia. - Silv 2019-08-17 00:02
@Silv tzn. chodzi ci o czyta te same obiekty w taki sam sposób? Bo jakby modyfikowała obiekty, to już nie za bardzo byłaby czysta że funkcja może być czysta, jeśli modyfikuje te same "obiekty" w taki sam sposób. - LukeJL 2019-08-17 12:57
Tfu, no, czyta. Tak. Dzięki. - Silv 2019-08-17 14:53

Pozostało 580 znaków

2019-08-16 18:35
2

@Silv ależ oczywiście, że @Maciej Cąderek ma rację, to jak przekazujesz argumenty nie ma najmniejszego znaczenia nt. tego czy funkcja jest czysta czy nie. Wszystko zależy od tego jak te dane potem obsługujesz. Przykład w D, gdzie można explicite zaznaczyć, że funkcja ma być czysta:

pure int add(in int a, in int b) { return a + b; }

https://ideone.com/GRqNNe

Funkcja przyjmuje dwie referencje do liczb całkowitych i zwraca nową liczbę. Jak najbardziej czysta funkcja.

edytowany 1x, ostatnio: hauleth, 2019-08-16 18:47
I wygląda mi na czystą (nie znam D). Ostrożnie się zgodzę. - Silv 2019-08-17 00:04
Chyba rzeczywiście nie ma znaczenia, jak przekazuje się argumenty – bo to zależy od języka. Tak więc język byłby nadrzędny w stosunku do oceny "czystości". - Silv 2019-08-17 00:07

Pozostało 580 znaków

2019-08-16 18:47
2

@Silv: "Co by jednak potwierdzało, że "czystość" zależy wyłącznie od języka" Jednak nie, w każdym (chyba) języku można napisać czystą funkcję (funkcja nie ma side effects i wartość zwracana zależy tylko od argumentów); sytuacja staje się śliska w językach imperatywnych, gdy przyjmujemy referencje do jakiś paskudnie mutowalnych obiektów, ale czystość dalej można osiągnąć jeśli stosujemy się do definicji.

EDIT @LukeJL "tak! :) nawet nie widząc twojego posta, zedytowałem swojego dodając do niego właśnie myśl, że jak zmienimy obiekt poza funkcją, to w rezultacie jest to inny już obiekt (w środku), więc funkcja dostaje inny argument (nawet jeśli o tej samej referencji) więc ma prawo się zachować inaczej..." Właśnie o to chodzi.


edytowany 2x, ostatnio: lion137, 2019-08-16 18:49
Identity function jest czysta w każdym języku ;) - hauleth 2019-08-16 19:01
Moja ulubiona funkcja:) - lion137 2019-08-16 19:10

Pozostało 580 znaków

2019-08-16 18:51
1

Moje rozumienie jest takie, że funkcja operuje na samych referencjach.

Nie wiem jak jest w C++, ale w przypadku JSa funkcja nie operuje na referencjach lecz zawsze na wartościach. owszem wartością może być referencja, ale jest to zasadnicza różnica - w JS taki kod nie zmodyfikuje ci zmiennej zewnętrznej:

const fn = obj => {
  obj = { foo: 2 }
}

const x = { foo: 1 }
fn(x)

console.log(x) // => { foo: 1 }
O, to, to. W każdym języku co innego może być czyste. - Silv 2019-08-17 00:05

Pozostało 580 znaków

2019-08-16 18:58
1
lion137 napisał(a):

@Silv: "Co by jednak potwierdzało, że "czystość" zależy wyłącznie od języka" Jednak nie, w każdym (chyba) języku można napisać czystą funkcję (funkcja nie ma side effects i wartość zwracana zależy tylko od argumentów); sytuacja staje się śliska w językach imperatywnych, gdy przyjmujemy referencje do jakiś paskudnie mutowalnych obiektów, ale czystość dalej można osiągnąć jeśli stosujemy się do definicji.

Gorzej jeśli obiekt mutuje się nie tylko się poza funkcją, ale zawiera jakieś gettery, które odpalają paskudne efekty uboczne. Tym sposobem uzyskujesz dostęp do właściwości, myślisz, że nic nie robisz, a się okazuje, że getter coś odpala.

Np. takie elementy DOM. Jeśli się spróbujesz dostać do jakiejś właściwości elementu DOM przez JS, to w rezultacie możesz spowodować kosztowne obliczenia layoutu w przeglądarce (jak to jest opisane na tej liście: https://gist.github.com/paulirish/5d52fb081b3570c81e3a )


((0b10*0b11*(0b10**0b101-0b10)**0b10+0b110)**0b10+(100-1)**0b10+0x10-1).toString(0b10**0b101+0b100);
Właśnie, czyli jest coraz gorzej, a podobno miało być lepiej:) - lion137 2019-08-16 19:15
bo ja wiem, czy coraz gorzej. Przecież DOM czy silniki przeglądarek to już stara technologia, tylko cały czas poprawiana. Jakby teraz ktoś wymyślił DOM i przeglądarkowe API od nowa to pewnie zrobiłby to inaczej niż ludzie, którzy byli przed nim. - LukeJL 2019-08-16 19:36
Ciekawa lista. :) - Silv 2019-08-17 00:38

Pozostało 580 znaków

2019-08-16 19:15
1

Btw, @Silv, pytanie - czy ta funkcja jest czysta?

/**
 * @param {number} a
 */
const fn = (a) => a * 2

Bo zgodnie z twoim tokiem rozumowania, wychodzi chyba na to, że nie.

Edit:

Tłumaczę dlaczego ta funkcja jest tak samo "problematyczna", jak funkcja przyjmująca wartość referencji do obiektu:

Weźmy takie wywołania funkcji:

/**
 * @param {number} a
 */
const fn = (a) => a * 2

let a = 1
const result1 = fn(a)
a = 2
const result2 = fn(a)

console.log(result1, result2)

Jaka jest różnica jakościowa między poprzednim przykładem a tym:

/**
 * @param {Object} a
 */
const fn = (a) => a.foo * 2

let a = {foo: 1}
const result1 = fn(a)
a.foo = 2
const result2 = fn(a)

console.log(result1, result2)

W obu wywołanie dwa razy z tą samą zmienną da różny rezultat.
To, że w drugim przykładzie mamy gdzieś po drodze referencję, jest przezroczyste dla funkcji (o ile nie próbujemy w funkcji modyfikować obiektu).

edytowany 4x, ostatnio: Maciej Cąderek, 2019-08-16 19:33
Chyba się zgubiłem, czemu nie? - lion137 2019-08-16 19:17
@lion137: Nie no imo jest czysta, dodałem edit czemu, jeśli dobrze rozumiem rozumowanie @Silv, prowdzi ono do takiego dziwnego wniosku - Maciej Cąderek 2019-08-16 19:32
Pierwsza funkcja jest czysta według mojego obecnego zdania (które wyklarowało się w tej dyskusji), nie rozumiem, jak niebycie przez nią czystą wynikałoby z mojego rozumowania. Dwa pozostałe listingi zaraz sprawdzę. - Silv 2019-08-17 00:09
Wszystko jest w porządku. Zgadzam się. Nadal nie widzę, skąd ten wniosek. Moje rozumowanie opiszę w poście, wzmiankuję Cię. - Silv 2019-08-17 00:11

Pozostało 580 znaków

2019-08-16 19:37
3

"W obu wywołanie dwa razy z tą samą zmienną da różny rezultat" Przecież nie Wywołałeś z tym samym argumentem. Za pierwszym razem Wywołałeś funkcję na obiekcie a. którego parametr foo miał wartość 1, a potem na a, którego foo wynosiło dwa; czyli Wywołałeś na dwu różnych argumentach.


Pozostało 580 znaków

2019-08-16 19:45
0

@lion137:

Przecież nie Wywołałeś z tym samym argumentem.

Bingo. Ta sama zmienna !== ten sam argument. Dokładnie to co opisałem wcześniej.

Pozostało 580 znaków

2019-08-16 19:47
2
Silv napisał(a):

Przejrzałem internet i obecnie mam następującą definicję czystej funkcji (niezależnie od języka programowania):

  1. Zwracana wartość (jeśli ją ma?) zależy wyłącznie od jej argumentów. — Mówiąc kolokwialnie, nie czyta z obiektu dostępnego z zewnątrz.
  2. Nie posiada skutków ubocznych (side effects). — Mówiąc kolokwialnie, nie pisze do obiektu dostępnego z zewnątrz.

Jednak zastanawia mnie pewna rzecz: z uwagi na punkt nr 1 wszystkie funkcje, które przyjmują argumenty przez referencję, nie będą czyste.

Coś ci nie wyszło tłumaczenie :) Zajrzyjmy do Wiki https://en.wikipedia.org/wiki/Pure_function :

In computer programming, a pure function is a function that has the following properties:[1][2]

  • Its return value is the same for the same arguments (no variation with local static variables, non-local variables, mutable reference arguments or input streams from I/O devices).
  • Its evaluation has no side effects (no mutation of local static variables, non-local variables, mutable reference arguments or I/O streams).

A więc stwierdzenie nie czyta z obiektu dostępnego z zewnątrz jest błędne. Poprawne stwierdzenie to - dla tych samych argumentów (włączając przypadek z brakiem argumentów) dwa wywołania mają zwrócić to samo. Jeżeli argumenty są różne to dwa wywołania mogą (ale nie muszą) zwrócić różne wartości. Czysta funkcja może odczytywać i często odczytuje stan, który nie jest przekazany jej przez argumenty. Przede wszystkim czysta funkcja może czytać do woli z globalnego niemutowalnego stanu (ale może się też domykać na lokalnym niemutowalnym stanie).


"Programs must be written for people to read, and only incidentally for machines to execute." - Abelson & Sussman, SICP, preface to the first edition
"Ci, co najbardziej pragną planować życie społeczne, gdyby im na to pozwolić, staliby się w najwyższym stopniu niebezpieczni i nietolerancyjni wobec planów życiowych innych ludzi. Często, tchnącego dobrocią i oddanego jakiejś sprawie idealistę, dzieli od fanatyka tylko mały krok."
Demokracja jest fajna, dopóki wygrywa twoja ulubiona partia.
Że tak siebie samego zacytuje z wcześniejszego posta "Mówiąc kolokwialnie, nie czyta z obiektu dostępnego z zewnątrz." jest błędne. Może czytać z obiektów zewnetrznych, pod warunkiem, że są niemutowalne (przez konwencję lub konstrukt języka). Inaczej domknięcia (integralny element FP) nie miałyby sensu. :D - Maciej Cąderek 2019-08-16 19:49
no ale nie uźródłowiłeś :P - Wibowit 2019-08-16 20:40
Dtatego dałem Ci łapkę :P - Maciej Cąderek 2019-08-16 20:42
Tak, jak w komentarzu pod postem @Maciej Cąderek. Rzeczywiście błędne. :) - Silv 2019-08-17 00:42

Pozostało 580 znaków

2019-08-16 20:03
0

2019, prawie 2020, a ludzie dalej nie odróżniają funkcji czystych od pół czystych LOOOL

edytowany 1x, ostatnio: WeiXiao, 2019-08-16 20:03
Pokaż pozostałe 2 komentarze
Z każdym dniem coraz bardziej utwierdzam się w przekonaniu, że to jest forum filozoficzne :D - Charles_Ray 2019-08-16 21:11
@Charles_Ray: A ja Cię nie pamiętam, a Ty tu już jesteś od 2008. :D - Silv 2019-08-17 00:44
Z przerwą 10 letnią, może dlatego ;) - Charles_Ray 2019-08-17 09:38

Pozostało 580 znaków

Odpowiedz
Liczba odpowiedzi na stronę

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