Una funzione è immediatamente impura se accetta una funzione come parametro?


17

Poiché la purezza di un parametro di input è sconosciuta fino al runtime, una funzione viene immediatamente considerata impura se accetta una funzione come parametro di input?

Correlati: se una funzione applica una funzione pura che è definita al di fuori della funzione, ma non viene passata come parametro, è ancora pura se soddisfa i criteri di non avere effetti collaterali e l'output dipende esclusivamente dall'input?

Per il contesto, sto scrivendo codice funzionale in JavaScript.


Come banale contro esempio, considera:foo = function(function bar){ print(bar.toString()) }
David dice Reinstate Monica il

1
@DavidGrinberg Questo non è un contro esempio, penso, ed evidenzia in realtà un problema più grande; se hai funzioni che possono essere sovrascritte e non puoi garantire che le implementazioni siano prive di effetti collaterali, non puoi garantire che la maggior parte delle funzioni che prendono oggetti e chiamano i loro metodi siano pure. Forse toString () di barra elimina alcuni file dal disco?
Joshua Taylor,

3
@DavidGrinberg Ma, penso che tu stia pensando in una buona direzione. foo = function(function bar) { return 3; } è puro e assume una funzione come argomento.
Joshua Taylor,

@JoshuaTaylor Il punto giusto, non ci ho pensato. Ma hai già sostanzialmente risolto il problema. Come soluzione alternativa, basta chiamare il 'root' toString()(cioè quello che potresti trovare sull'oggetto Java).
David dice di reintegrare Monica il

Risposte:


22

Fintanto che tutti i valori utilizzati nella funzione sono definiti esclusivamente dai suoi parametri, è una funzione pura.

L'aspetto che l'output è lo stesso ogni volta per lo stesso input è controllato dal fatto che i parametri siano puri. Se si presume che anche i parametri (come un argomento di funzione) siano pure, è puro.

In un linguaggio come Javascript in cui la purezza non viene applicata, ciò significa che è possibile fare in modo che una funzione altrimenti pura abbia un comportamento impuro invocando una funzione impura passata come parametro.

Ciò significa effettivamente che per i linguaggi che non impongono la purezza (cioè quasi tutti), è impossibile definire una funzione pura che invoca funzioni passate come argomenti. È ancora utile scriverli il più puro possibile e ragionare su di essi come funzioni pure, ma devi fare attenzione perché il presupposto che sia puro verrà rotto se passi argomenti sbagliati.

Nella mia esperienza pratica, di solito non è un grosso problema: trovo raro che funzioni impure vengano utilizzate come argomenti di funzioni per funzioni pure.


Per quanto riguarda la tua affermazione che "Fintanto che tutti i valori utilizzati nella funzione sono definiti esclusivamente dai suoi parametri, è una funzione pura". Cosa succede nel caso delle costanti? Se ho una funzione areaOfCircle r => Math.Pi * r * r, areaOfCirclenon sarà puro in quanto non utilizza solo i parametri?
David Arno,

2
@DavidArno Questo è un punto giusto. Secondo la trasparenza referenziale, fare riferimento a un valore esterno statico non sarebbe diverso dal fatto che fosse codificato, quindi sarebbe comunque puro.
Daenyth,

1
"questo significa che è possibile fare in modo che una funzione pura abbia un comportamento impuro" - per definizione, una funzione pura non può avere un comportamento impuro. Stai commettendo l'errore di pensare a una funzione f(f2)che invoca f2non si basa transitivamente su nulla f2su cui si basa. Una funzione che potrebbe invocare funzioni passate arbitrarie non è pura.
user2357112 supporta Monica il

2
@Daenyth: meglio, ma presuppone comunque che la funzione debba invocare la funzione pass-in. Potrebbe essere qualcosa del genere function compose(f, g) {return function h(x) {return f(g(x));};}, che è puro nonostante abbia assunto funzioni come argomenti.
user2357112 supporta Monica il

1
"Trovo raro che funzioni impure possano essere usate come argomenti di funzioni per funzioni pure." - non è un linguaggio funzionale, ma alcune funzioni della libreria C ++ hanno avvertimenti specifici che gli argomenti predicati devono essere (qualche approssimazione a) puri. Quindi, in un certo senso, ciò significa che non è solo raro, non succede mai validamente. Ma in un altro senso il fatto che devono vietarlo è perché le persone a volte vogliono farlo. Ad esempio vogliono passare a una findroutine un predicato impuro che ritorni "vero" per il terzo oggetto corrispondente che incontra, o per alcune assurdità.
Steve Jessop,

19

Poiché la purezza di un parametro di input è sconosciuta fino al runtime, una funzione viene immediatamente considerata impura se accetta una funzione come parametro di input?

No. controesempio:

function pure(other_function) {
    return 1;
}

Non importa se other_functionè una funzione pura, una funzione impura o non una funzione. La purefunzione è pura.

Altro controesempio:

function identity(x) {
    return x;
}

Questa funzione è pura, anche se xè una funzione impura. identity(impure_function)tornerà sempre impure_function, non importa quante volte si ripete la chiamata. Non importa se identity(impure_function)()restituisce sempre la stessa cosa; il valore restituito di una funzione non influisce sulla sua purezza.


In generale, se una funzione può chiamare una funzione che è stata passata come argomento, non è pura. Ad esempio, una funzione function call(f) {f();}non è pura, perché anche se non fa menzione di alcuno stato globale o mutevole, fpotrebbe essere qualcosa del genere alertche causa effetti collaterali visibili.

Se una funzione accetta funzioni come argomenti, ma non le chiama o non le fa chiamare, allora può essere pura. Potrebbe essere ancora impuro se fa qualche altra cosa impura. Ad esempio, function f(ignored_function) {alert('This isn't pure.');}è impuro, anche se non chiama mai ignored_function.


4
Questa risposta sembra eccessivamente pedante. Possiamo dedurre dalla domanda che l'interesse riguarda il parametro della funzione che viene invocato. L'esistenza di funzioni che possono / fanno assumere altre funzioni come parametro senza invocarle non influisce su questa domanda.
Walpen

13
@walpen: la domanda non fa menzione dell'invocazione dell'argomento. Non c'è motivo di supporre che l'interrogatore abbia persino capito che una funzione potrebbe assumere un'altra funzione come input senza invocarla. È importante sottolineare ipotesi nascoste come questa, piuttosto che supporre che tu debba assumerle.
user2357112 supporta Monica il

12

Poiché la purezza di un parametro di input è sconosciuta fino al runtime, una funzione viene immediatamente considerata impura se accetta una funzione come parametro di input?

Tecnicamente sì, a meno che non ci sia un modo nella tua lingua per garantire che anche la funzione di input sia pura.

se una funzione applica una funzione pura che è definita al di fuori della funzione, ma non viene passata come parametro, è ancora pura se soddisfa i criteri di non avere effetti collaterali e l'output dipende esclusivamente dall'input?

Sì. Quindi concentriamoci su ciò che conta qui. Chiamare una funzione pura o no non è di per sé utile. Le funzioni pure sono utili perché produrre lo stesso output per qualsiasi input e non dipendere dallo stato o avere effetti collaterali è un insieme molto utile di proprietà. Significa che una volta che la tua funzione è stata eseguita, puoi "ricordare" la risposta per quell'input e sarà sempre vera. Inoltre, non è necessario eseguire nuovamente la funzione per generare effetti collaterali. E puoi eseguire quella funzione in parallelo (o fuori servizio) con altre funzioni e sapere che non avranno interazioni nascoste che si comportano male.

Queste proprietà utili rimangono valide se la funzione utilizza altre funzioni di sola lettura per svolgere il proprio lavoro, indipendentemente da come le fa riferimento.


5

Come ha detto Telastyn: tecnicamente sì, a meno che non ci sia un modo nella tua lingua per garantire che anche la funzione di input sia pura.

Non è ipotetico, ci sono davvero buoni modi per garantirlo. Almeno in una lingua fortemente tipizzata.

Una funzione così pura che scriveresti in JavaScript come

function foo(f) {
   return f(1) + 2;
}

può essere tradotto direttamente in Haskell:

foo :: (Int -> Int) -> Int
foo f = f 1 + 2

Ora, in JavaScript puoi fare cose cattive come

js> foo (function(x) {console.log("muharhar"); return 0})
muharhar
2

Questo non è possibile in Haskell . Il motivo è che qualcosa di simile agli effetti collaterali console.log()deve sempre avere un tipo di risultato IO something, non somethingsolo.

GHCi> foo (\x -> print "muarhar" >> return 0)

<interactive>:7:12:
    Couldn't match expected type ‘Int’ with actual type ‘IO b0’
    In the expression: print "muarhar" >> return 0
    In the first argument of ‘foo’, namely
      ‘(\ x -> print "muarhar" >> return 0)’
    In the expression: foo (\ x -> print "muarhar" >> return 0)

Affinché l'espressione sia corretta, dovremmo fornire foola firma del tipo

foo :: (Int -> IO Int) -> Int

Ma poi non riesco più ad implementarlo: poiché la funzione argomento ha IOcome risultato, non posso usarlo all'interno foo.

<interactive>:8:44:
    Couldn't match expected type ‘Int’ with actual type ‘IO Int’
    In the first argument of ‘(+)’, namely ‘f 1’
    In the expression: f 1 + 2

L'unico modo in cui potrei usare IOun'azione fooè se il risultato di fooha il tipo IO Intstesso:

foo :: (Int -> IO Int) -> IO Int
foo f = do
   f1 <- f 1
   return (f1 + 2)

Ma a questo punto dalla firma emerge chiaramente fooche non è nemmeno una funzione pura.


1
Prima di dire "impossibile", dai un'occhiata a unsafeIO:-)
Bergi,

2
@Bergi: in realtà non fa parte di Haskell, ma della sua interfaccia di funzione esterna: per consentire di affermare che una funzione definita in qualche altra lingua è pura, che il compilatore di Haskell ovviamente non può inferire dalla firma del tipo perché altre lingue generalmente non hanno cosa come IO. Per inciso, può anche essere usato per causare confusione nascondendo effetti collaterali in una funzione "pura", ma questo è davvero pericoloso in Haskell perché non c'è davvero un modo affidabile per specificare l'ordine di valutazione delle funzioni pure.
lasciato il

Sì è vero. Tuttavia, penso di averlo visto essere usato per "nascondere" effetti collaterali benefici anche in una funzione "pura", dove prima doveva essere stabilita la sicurezza dell'approccio.
Bergi,

@Bergi Per lo più non dovresti usare unsafeIO; questo è un trampolino di fuga di ultima istanza che elude il tipo di sistema garantito, e quindi il tuo non è un buon punto.
Andres F.

0

No non lo è.

Se la funzione passata è impura E la tua funzione chiama la funzione passata, la tua funzione verrà considerata impura.

La relazione pura / impura è un po 'come sync / async in JS. Puoi usare liberamente il codice puro da impuro, ma non viceversa.


Questa risposta non aggiunge nulla che non sia già stato spiegato in questo ... Leggi le risposte precedenti prima di riformulare le cose :)
Andres F.

Che dire dell'analogia sync / async?
Bobby Marinoff
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.