Una funzione impura può essere referenzialmente trasparente?

5

Una funzione totale impura è considerata Referentially Transparent (RT)?

Esempio:

// Given an 'id', try to retrieve an optional Person
// from the database, i.e. side-effect.
// Note that 'String' is a poor choice for the 'Left',
// but it's not important for this question
def getPerson(id: Long): Either[String, Option[Person]] = 
    // ignore implementation

Supponendo che getPerson sia implementato per restituire sempre un Either , cioè ignorando eventuali errori fatali (Memoria insufficiente, Stack Overflow, ecc.), è considerato RT?

Forse la possibilità di un errore di Memoria insufficiente o di overflow dello stack si interrompe su RT?

Ho letto link , ma non sono sicuro della risposta alla mia domanda.

    
posta Kevin Meredith 25.01.2017 - 05:02
fonte

2 risposte

9

La definizione di RT che utilizziamo in "il libro rosso" è la seguente:

An expression e is referentially transparent if for every program p, every occurrence of e in p can be replaced by the result of evaluating e without affecting the meaning of p.

Quindi l'espressione getPerson(0) è considerata RT se ovunque vedi getPerson(0) , potresti sostituirla con il risultato della chiamata.

Se puoi farlo dipende dall'implementazione. Se l'implementazione contiene una mappa immutabile da Long a Person , ed è semplicemente una ricerca in quella mappa che non cambia, quindi getPerson(x) è RT per qualsiasi x e getPerson è considerato un puro la funzione .

Se invece la chiamata a getPerson(x) effettua una connessione a un database che varia nel tempo attraverso una rete, il risultato cambierà a seconda del contesto. In altre parole, quindi getPerson(x) è non puramente una funzione di x .

Riguardo alla tua altra domanda, se OOME e SOE si rompono in RT, la risposta diventa un po 'più sfumata. Si potrebbe sostenere che nessuna espressione di Scala complessa è RT . Prendere in considerazione:

case class Foo(s: String)

Ora, Foo("hello") == Foo("hello") . Ma ogni due espressioni che valutano in Foo("hello") sono notevolmente diverse, perché Foo("hello") eq Foo("hello") è false . Quindi data l'esistenza di eq , l'espressione Foo("hello") non è RT.

Dovremo modificare un po 'la nostra definizione di RT:

An expression e is referentially transparent with regard to a program p if every occurrence of e in p can be replaced by the result of evaluating e without affecting the meaning of p.

Con questa definizione, la programmazione funzionale diventa un problema di ottimizzazione piuttosto che assoluto. Cioè, siamo interessati alla programmazione usando espressioni che sono RT rispetto a quanti più programmi possibili.

Quindi quando diciamo "RT", è davvero implicito che intendiamo "RT per quanto riguarda la classe di programmi che non usano eq , o esauriscono la memoria, o eseguono il dump core, o entrano in un infinito ciclo ".

    
risposta data 25.01.2017 - 18:23
fonte
7

La trasparenza referenziale non è un concetto definito formalmente ma

usually means that an expression always evaluates to the same result in any context. Source

Ecco un semplice test per la trasparenza referenziale per un'espressione e : fa inline una dichiarazione di e modifica (potenziale) comportamento?

Cioè, c'è una differenza tra:

val x = e
doFoo(x)
doBar(x)

e il seguente

doFoo(e)
doBar(e)

In questo caso c'è un'enorme differenza! Ad esempio, questo codice funzionerà correttamente:

val user = getPerson(32)
deleteUser(user)
displayUserDeletion(user)

mentre questo codice esploderà

deleteUser(getPerson(32))
displayUserDeletion(getPerson(32))

Quindi questo non è sicuramente referenzialmente trasparente. Per quanto riguarda la tua seconda domanda:

Perhaps the possibility of an Out of Memory or Stack Overflow error breaks RT?

Di solito quando pensiamo alla trasparenza referenziale o veramente a qualsiasi proprietà dei nostri programmi, pensiamo a loro come a macchine perfette con memoria infinita. Questa assunzione semplificativa è ovviamente sbagliata, ma davvero necessaria per parlare ovunque dei programmi. Fondamentalmente, stack overflow e OOM "break RT", quindi RT è sempre interrotta perché praticamente per qualsiasi espressione e esiste un computer con un'espressione sufficientemente piccola tale che

val x = e
doFoo(x)
doBar(x)

funziona ma

doFoo(e)
doFoo(e)

esaurisce la memoria. Per evitare che la trasparenza referenziale sia un concetto inutile, dobbiamo ignorare la limitazione delle nostre runtime.

Questa definizione mostra anche perché dovremmo preoccuparci di RT, poiché consente ottimizzazioni potenti come l'eliminazione di sottoespressione comune ( i compilatori Haskell lo fanno ) dove se e è RT possiamo fare in modo che venga valutata solo una volta, ad es trasformando

doFoo(e)
doBar(e)

in

val x = e
doFoo(x)
doBar(x)

Per quanto riguarda la domanda nel titolo, ci sono in realtà funzioni impure che sono referenzialmente trasparenti, ma è piuttosto una circostanza insolita:

  • Supponiamo di avere un oggetto mutabile S che ha solo un metodo per modificarlo, S.f() , cioè è idempotente. In questo caso S.f() è impuro (cambia stato) ma è referenzialmente trasparente.

Per rendere questo più concreto, supponiamo di avere

class Foo {                     
  private var _x = 0
  def this(x: Int) { this(); _x = x }
  def x = _x
  def setXToFive(): Unit = {
    _x = 5
  } 
}

Qui se foo : Foo , foo.setXToFive() è referenzialmente trasparente.

    
risposta data 25.01.2017 - 06:02
fonte

Leggi altre domande sui tag