Come si chiama una funzione in cui lo stesso input restituirà sempre lo stesso output, ma ha anche effetti collaterali?

39

Diciamo che abbiamo una normale funzione pura come

function add(a, b) {
  return a + b
}

E poi lo alteriamo in modo tale che abbia un effetto collaterale

function add(a, b) {
  writeToDatabase(Math.random())
  return a + b;
}

Per quanto ne so non è considerato una pura funzione perché spesso sento che le persone chiamano pure funzioni "funzioni senza effetti collaterali". Tuttavia, si comporta come una pura funzione per quanto riguarda il fatto che restituirà lo stesso output per gli stessi input.

Esiste un nome diverso per questo tipo di funzione, è senza nome o è ancora puro e mi sto sbagliando riguardo alla definizione di purezza?

    
posta m0meni 30.04.2016 - 23:21
fonte

7 risposte

83

Non sono sicuro delle definizioni universali di purezza, ma dal punto di vista di Haskell (un linguaggio in cui i programmatori tendono a preoccuparsi di cose come la purezza e la trasparenza referenziale), solo la prima delle tue funzioni è "puro". La seconda versione di add non è pura . Quindi, in risposta alla tua domanda, la definirei "impura";)

Secondo questa definizione, una funzione pura è una funzione che:

  1. Dipende solo dal suo input. Cioè, dato lo stesso input, restituirà sempre lo stesso output.
  2. È referenzialmente trasparente: la funzione può essere liberamente sostituita dal suo valore e il "comportamento" del programma non cambierà.

Con questa definizione, è chiaro che la tua seconda funzione non può essere considerata pura, poiché infrange la regola 2. Cioè, i seguenti due programmi NON sono equivalenti:

function f(a, b) { 
    return add(a, b) + add(a, b);
}

e

function g(a, b) {
    c = add(a, b);
    return c + c;
}

Questo perché anche se entrambe le funzioni restituiscono lo stesso valore, la funzione f scriverà nel database due volte ma g scriverà una sola volta! È molto probabile che le scritture nel database facciano parte del comportamento osservabile del tuo programma, nel qual caso ho mostrato che la tua seconda versione di add non è "pura".

Se le scritture nel database non sono una parte osservabile del comportamento del tuo programma, allora entrambe le versioni di add possono essere considerate equivalenti e pure. Ma non riesco a pensare a uno scenario in cui scrivere sul database non ha importanza. Anche la registrazione conta!

    
risposta data 01.05.2016 - 00:21
fonte
19

What do you call a function [for which] the same input will always return the same output, but also has side effects?

Tale funzione è chiamata

deterministic

An algorithm whose behavior can be completely predicted from the input.

termwiki.com

Per quanto riguarda lo stato:

A seconda della definizione di una funzione che usi, una funzione non ha uno stato. Se vieni dal mondo orientato agli oggetti, ricorda che x.f(y) è un metodo. Come una funzione sembrerebbe f(x,y) . E se ti piacciono le chiusure con ambito lessicale incluso, ricorda che lo stato immutabile potrebbe anche far parte dell'espressione di funzioni. È solo uno stato mutabile che avrebbe un impatto sulla natura deterministica delle funzioni. Quindi f (x) = x + 1 è deterministico fintanto che 1 non cambia. Non importa dove viene memorizzato 1.

Le tue funzioni sono entrambe deterministiche. Il tuo primo è anche una pura funzione. Il tuo secondo non è puro.

Pure function

  1. The function always evaluates the same result value given the same argument value(s). The function result value cannot depend on any hidden information or state that may change while program execution proceeds or between different executions of the program, nor can it depend on any external input from I/O devices.

  2. Evaluation of the result does not cause any semantically observable side effect or output, such as mutation of mutable objects or output to I/O devices.

wikipedia.org

Il punto 1 significa deterministico . Il punto 2 significa trasparenza referenziale . Insieme significano che una funzione pura consente solo ai suoi argomenti e al suo valore restituito di cambiare. Nient'altro causa cambiamenti. Nient'altro è cambiato.

    
risposta data 02.05.2016 - 15:17
fonte
8

Se non ti interessa l'effetto collaterale, allora è referenzialmente trasparente. Naturalmente è possibile che non ti interessi, ma qualcun altro lo fa, quindi l'applicabilità del termine dipende dal contesto.

Non conosco un termine generico per indicare esattamente le proprietà che descrivi, ma un sottoinsieme importante sono quelli che sono idempotenti . In informatica, in modo leggermente diverso in matematica *, una funzione idempotente è quella che può essere ripetuta con lo stesso effetto; vale a dire che il risultato netto dell'effetto collaterale di farlo molte volte equivale a farlo una volta.

Quindi, se il tuo effetto collaterale fosse aggiornare un database con un certo valore in una certa riga, o creare un file con contenuti esattamente coerenti, sarebbe idempotente , ma se lo fosse aggiunto al database o aggiunto a un file, quindi non lo sarebbe.

Le combinazioni di funzioni idempotent possono o non possono essere idempotenti nel loro complesso.

* L'uso di idempotent in informatica diversa dalla matematica sembra derivare da un uso non corretto del termine matematico che è stato poi adottato perché il concetto è utile.

    
risposta data 01.05.2016 - 14:32
fonte
3

Non so come si chiamino tali funzioni (o se ci sia anche un nome sistematico), ma chiamerei la funzione che non è pura (poiché altre risposte si nascondono) ma restituisce sempre lo stesso risultato se fornita con gli stessi parametri " funzione dei suoi parametri "(rispetto alla funzione dei suoi parametri e qualche altro stato). Lo chiamerei solo funzione, ma sfortunatamente quando diciamo "funzione" nel contesto della programmazione, intendiamo qualcosa che non deve essere affatto funzione effettiva.

    
risposta data 01.05.2016 - 01:02
fonte
2

Dipende fondamentalmente dal fatto che ti interessi o meno l'impurità. Se la semantica di questa tabella è che non ti interessa quante voci ci sono, allora è pura. Altrimenti, non è puro.

O per dirla in altro modo, va bene finché le ottimizzazioni basate sulla purezza non interrompono la semantica del programma.

Un esempio più realistico sarebbe se stessimo cercando di eseguire il debug di questa funzione e aggiunto le dichiarazioni di registrazione. Tecnicamente, la registrazione è un effetto collaterale. I registri lo rendono impuro? No.

    
risposta data 30.04.2016 - 23:36
fonte
1

Direi che la cosa migliore da fare non è come la chiameremmo, ma come dovremmo analizzare una tale parte di codice. E la mia prima domanda chiave in tale analisi sarebbe:

  • L'effetto collaterale dipende dall'argomento della funzione o dal risultato dell'effetto collaterale?
    • No: la "funzione efficace" può essere rifatta in una funzione pura, un'azione efficace e un meccanismo per combinarli.
    • Sì: la "funzione efficace" è una funzione che produce un risultato monadico.

Questo è semplice da illustrare in Haskell (e questa frase è solo una mezza barzelletta). Un esempio di "no" potrebbe essere qualcosa del tipo:

double :: Num a => a -> IO a
double x = do
  putStrLn "I'm doubling some number"
  return (x*2)

In questo esempio l'azione che eseguiamo (stampa la riga "I'm doubling some number" ) non ha alcun impatto sulla relazione tra x e il risultato. Ciò significa che possiamo refactoring in questo modo (utilizzando la classe Applicative e il suo operatore *> ), che mostra che la funzione e l'effetto sono in realtà ortogonali:

double :: Num a => a -> IO a
double x = action *> pure (function x)
  where
    -- The pure function 
    function x = x*2  
    -- The side effect
    action = putStrLn "I'm doubling some number"

Quindi in questo caso, personalmente, direi che è un caso in cui è possibile calcolare una funzione pura. Gran parte della programmazione di Haskell riguarda proprio questo: imparare a calcolare le parti pure dal codice efficace.

Un esempio di tipo "sì", in cui le parti pure e quelle effettive non sono ortogonali:

double :: Num a => a -> IO a
double x = do
  putStrLn ("I'm doubling the number " ++ show x)
  return (x*2)

Ora, la stringa da stampare dipende dal valore di x . La parte funzione (moltiplica x per due), tuttavia, non dipende affatto dall'effetto, quindi possiamo ancora considerarlo:

logged :: (a -> b) -> (a -> IO x) -> IO b
logged function logger a = do
  logger a
  return (function a)

double x = logged function logger
  where function = (*2) 
        logger x putStrLn ("I'm doubling the number " ++ show x)

Potrei continuare a sillabare altri esempi, ma spero che questo sia sufficiente per illustrare il punto con cui ho iniziato: non lo si "chiama" qualcosa, si analizza come le parti pure e quelle effettive si relazionano e si valutano quando è a tuo vantaggio.

Questo è uno dei motivi per cui Haskell utilizza la sua classe Monad in modo estensivo. Le Monade sono (tra le altre cose) uno strumento per eseguire questo tipo di analisi e refactoring.

    
risposta data 10.05.2016 - 20:36
fonte
-3

Le funzioni che hanno lo scopo di causare effetti collaterali sono spesso definite effectful . Esempio link

    
risposta data 02.05.2016 - 11:00
fonte

Leggi altre domande sui tag