Esiste un concetto di variabile con fasi di inizializzazione: non inizializzato, inizializzato, immutabile?

1

Stavo guardando questo thread su Stack Overflow e ho pensato alla programmazione funzionale che sono stato l'apprendimento, e quanto l'immutabilità sia così importante lì, e mi è venuto in mente che forse un linguaggio ha avuto l'idea di far partire le variabili "null", quindi passare a uno stato di "costruzione" e infine diventare le stesse di qualsiasi altro immutabile quando l'inizializzazione è terminata.

È un concetto valido e / o è stato provato o sperimentato a livello di lingua?

Potrebbe essere qualcosa del genere:

string s new String(); // empty strings start out mutable
for x = 1 to 10
  s = s + "z"
s.Freeze() // now it's immutable

È solo un pensiero .. forse non è buono, ma sembra un ibrido di due cose che sembrano contrapporsi l'un l'altra.

    
posta Aaron Anodide 04.07.2013 - 00:16
fonte

4 risposte

2

Stai confondendo concetti, nel tuo codice la variabile "s" è sempre immutabile e in nessun momento smette di essere immutabile.

String s new String(); // Strings are ummutable

    for x = 1 to 10
      s = s + "z"   // you are destroying an object, creating 
                    // another one and assining it to the 
                    // reference. You are not changing the 
                    // internal state of the String.
      s.Freeze()    // It makes no sense. The only way to avoid 
                    // assigning a new value to a variable is 
                    // converting it into a constant. But then you can only
            // assign an initial value when you declare it. 

Immutabilità significa non essere in grado di alterare lo stato interno di un oggetto tramite proprietà, soluzioni o metodi.

In Java, la classe String è immutabile, ovvero non ha setter o altri metodi per modificarne lo stato.

La classe StringBuffers è mutabile, ha un metodo Append() e altri che cambiano il suo stato interno.

La classe String è immutabile non significa che non puoi sovrascrivere un oggetto String e assegnare un'altra stringa al riferimento.

Essendo chiaro, puoi sicuramente programmare una classe i cui setter alzano e IllegalStateException se provi a impostare un valore dopo che lo hai "congelato".

public class FreezableThing {
    private String name="";
    private int number=0;
    boolean frozen = false;

    public boolean isFrozen() { return frozen; }

    public void setFrozen(boolean frozen) { this.frozen = frozen; }

    public String getName() { return name; }

    public int getNumber() { return number; }

    public void setName(String name) {
        if (this.frozen){
            throw new IllegalStateException("Frozen object cannot be changed");
        }
        this.name = name;
    }

    public void setNumber(int number) {
        if (this.frozen){
            throw new IllegalStateException("Frozen object cannot be changed");
        }       
        this.number = number;
    }

}

...

    
risposta data 04.07.2013 - 15:23
fonte
2

Il pattern che descrivi somiglia molto a un costruttore nella programmazione orientata agli oggetti; mentre i campi non sono necessariamente immutabili, è pratica comune costruire i propri oggetti come immutabili tranne che per i costruttori, ad esempio in un ipotetico linguaggio OOP:

class NumberSequenceString {
    private string val;

    public function __construct(top) {
        val = "";
        for (i = 1; i <= top; i++)
            val = val + (string)i;
        val = val + "z";
    }

    public getVal() { return val; }
}

var myString = new NumberSequenceString(10);

(Mi sono preso la libertà di rendere il tuo codice un po 'più interessante: invece di ripetere "z" 10 volte, ora sputa la stringa "12345678910z" , il codice richiesto per costruire questa stringa è abbastanza complesso da dimostrare elaborazione non banale in seguito).

Tuttavia, la maggior parte degli idiomi di programmazione funzionale prende una strada diversa; invece di distinguere tra le fasi "non inizializzate", "costruttive" e "immutabili" del ciclo di vita di una variabile, eliminano solo le prime due: il ciclo di vita di una variabile inizia quando viene definito e la definizione fornisce già l'unico valore che sarà tutti hanno. Questo è possibile perché l'idioma di programmazione funzionale favorisce potenti espressioni su istruzioni , il che significa che il valore che si assegna può essere costruito in una sola espressione; invece di utilizzare loop iterativi con una variabile variabile mutabile, i programmatori funzionali pensano in termini di map e reduce . Il tuo esempio in stile funzionale sarebbe simile a questo:

function makeSequenceString(top) {
    return range(1, top).map(toString).reduce(+) + "z";
}

const myString = makeSequenceString(10);

O, più idiomaticamente, in una sorta di sintassi pseudo-Haskell / ML / F #:

makeSequenceString top = (reduce (+) (map toString [1..top])) + "z"
myString = makeSequenceString 10

Si noti che la parte che costruisce il valore è solo una grande espressione e la funzione makeSequenceString non fa assolutamente nulla ma valuta l'espressione e restituisce il suo valore. E poiché assegniamo questo valore immediatamente come parte della definizione di myString , non c'è alcun punto nel programma in cui myString non è inizializzato o "in costruzione" - non appena esiste, ha un valore, e questo è l'unico valore che avrà (almeno fino a quando il programmatore si attacca con uno stile puramente funzionale).

    
risposta data 04.07.2013 - 10:16
fonte
1

Il lavoro su typestate ha introdotto modi per un sistema di tipi per tenere traccia di quali operazioni sono consentite su un pezzo di dati mentre lo stato dei dati cambia nel tempo. Il documento originale utilizzato typesafe per inizializzare e finalizzare le variabili in modo sicuro. Lavoro successivo applicato typestate a proprietà di alto livello, come tenere traccia di se un il file è aperto o chiuso. Plaid utilizza typestate.

In una lingua con typestate, gli oggetti hanno un tipo in ogni punto del programma . Le funzioni possono modificare i tipi di oggetti a cui accedono. Le regole di digitazione della lingua assicurano che le variabili vengano sempre utilizzate in modo coerente con il loro tipo, in modo che i tentativi di utilizzare una variabile non inizializzata o di leggere da un file chiuso vengano catturati in fase di compilazione.

    
risposta data 04.07.2013 - 18:27
fonte
1

Il problema con il tuo approccio è la sicurezza del tipo statico:

string s new String(); // empty strings start out mutable
for x = 1 to 10
  s = s + "z"
if (rand()==0) s.Freeze() // now it might be immutable

E la soluzione esplicita a questo è una coppia come String / StringBuilder. Uno è immutabile e longevo, l'altro è usato nella costruzione e di breve durata. Il tuo "Freeze ()" diventa l'assegnazione dal costruttore all'oggetto immutabile.

    
risposta data 05.07.2013 - 09:41
fonte

Leggi altre domande sui tag