Czy funkcja otrzymująca wartość z innej funkcji jest uważana za czystą?

9

Próbuję znaleźć sposób obsługi domyślnych wartości zmiennych podczas wykonywania funkcji bez skutków ubocznych i skończyło się na tym, że:

function getDefaultSeparator() {
    return ':';
}

function process(input, separator) {
    var separator = separator || getDefaultSeparator();

    // Use separator in some logic

    return output;
}

Domyślny separator będzie używany w innych funkcjach i chcę go zdefiniować tylko w jednym miejscu.

Jeśli jest to czysta funkcja, jaka jest różnica po prostu zamiast używania globalnej stałej DEFAULT_SEPARATOR?

agost
źródło
5
Nie ma żadnej istotnej różnicy, chyba że planujesz użyć funkcji jako symbolu zastępczego dla pewnej logiki, która zostanie dodana później.
Robert Harvey
3
Możliwy duplikat Czy funkcja jest natychmiast nieczysta, jeśli przyjmuje funkcję jako parametr? . Pytanie nie jest dokładnym duplikatem, ale odpowiedź powinna być taka sama. („To zależy od czystości drugiej funkcji.”)
jpmc26
1
Użycie stałej globalnej nie powoduje, że funkcja jest nieczysta. Używanie wartości globalnej, którą zakładasz, jest stała, robi.
chepner
Przy okazji możesz curry process(z odwróconą kolejnością parametrów) i specjalizować funkcję curry wvar processDefault = process(":")
bob

Odpowiedzi:

22

Czy funkcja otrzymująca wartość z innej funkcji jest uważana za czystą?

To zależy od tego, co robi druga funkcja i od tego, co robi funkcja wywołująca. Zanieczyszczenie jest zakaźne, czystość nie.

Wywołanie funkcji czystej nie zmienia czystości funkcji wywołującej. Wywołanie funkcji nieczystości automatycznie powoduje, że funkcja wywoływania jest również nieczysta.

Tak więc w twoim przykładzie zależy to od czystości części, którą pominąłeś: jeśli jest czysta, to cała funkcja jest czysta.

Jeśli jest to czysta funkcja, jaka jest różnica po prostu zamiast używania globalnej stałej DEFAULT_SEPARATOR?

Nic. Funkcja, która zawsze zwraca tę samą wartość, jest nie do odróżnienia od stałej. W rzeczywistości dokładnie tak modeluje się stałe w rachunku λ.

Jörg W Mittag
źródło
2
„Wywołanie funkcji nieczystości automatycznie powoduje, że funkcja wywołania jest nieczysta”. Czy jesteś tego pewien? AFAICS, wywoływanie nieczystej funkcji nie powoduje, że osoba dzwoniąca jest nieczysta, chociaż może to zrobić.
Deduplicator
2
@Deduplicator: zależy od tego, ile analiz statycznych możesz (przeszkadza) zrobić. Jasne, jeśli istnieje funkcja, funcktóra ma skutki uboczne, gdy podasz 0, ale nie podasz 1, możesz rozsądnie powiedzieć, że chociaż funcsama „jest nieczysta”, funkcja nazywa ją jako func(1)(i ignorując wartość zwracaną, powiedzmy) niekoniecznie jest nieczysty. Wywołanie funcwystarczy, aby „skażić” dzwoniącego jako potencjalnie nieczystego, ale skażona funkcja może w pewnym sensie zostać potwierdzona jako czysta. Przynajmniej w javascript, gdzie czysty / nieczysty nie jest zdefiniowany w języku.
Steve Jessop
6

Tak, są to obie funkcje czyste (przy założeniu, że część elided jest również czysta), ponieważ:

  1. Wynik zależy tylko od parametrów.
  2. Brak efektów ubocznych.

Zauważ, że gdyby getDefaultSeparator()nie była to czysta funkcja, żadna z nich nie byłaby process()czysta.

W Javascripcie nie ma znaczącej różnicy między używaniem funkcji czystej lub stałej i obie mogą być używane przez funkcję czystą, o ile unika się zdolności Javascript do redefiniowania funkcji lub zmiany wartości stałych.

Kluczową koncepcją czystych funkcji jest to, że można je zastąpić wartością, którą zwracają, bez wpływu na wyniki programu.

8bittree
źródło
1

Jak mówią inni, z pewnością nadal jest to czysta funkcja.

Porozmawiajmy jednak o problemach projektowych. Masz rację, próbując coś zrobić, aby zachować kod DRY, umieszczając wartość tylko w jednym miejscu. Ponadto moim zdaniem należy również wziąć pod uwagę odpowiedni poziom sprzężenia.

Korzystanie z funkcji daje większą elastyczność zmiany implementacji, co oznacza, że ​​podejście funkcyjne oferuje luźniejsze sprzężenie niż zmienna globalna.

Pytanie brzmi, czy ktoś tego potrzebuje, czy nie?

Jeśli konsumenci i dostawca są w tym samym module, a dostawca jest prywatny dla modułu, trudno argumentować, że ten poziom luźnego sprzężenia jest konieczny, ponieważ jeśli dostawca wymaga aktualizacji ze zmiennej prywatnej na zmienną metodą prywatną, proste refaktoryzacja w module może być zastosowana do konsumentów w tym samym czasie. Korzystanie z metody / funkcji, zanim naprawdę będziesz potrzebować, może podlegać YAGNI.

Nawet jeśli konsumenci i dostawca są w różnych modułach, ale moduły są wersjonowane razem (na przykład używasz minifyera, aby moduły konsumenta i dostawcy były w tym samym pliku), może również obowiązywać YAGNI.

Z drugiej strony, jeśli na przykład producent znajduje się w bibliotece lub pakiecie API lub module, który jest wersjonowany oddzielnie od konsumenta (ów), użycie tej funkcji może być odpowiednie. W takim przypadku powinniśmy patrzeć na długowieczność API i zasady takie jak OCP.

(Z drugiej strony, jeśli twój kod ma znaczny rozmiar, zachęcam do używania modułów z polami i metodami zamiast zmiennych globalnych i funkcji).

Erik Eidt
źródło