Se è possibile utilizzare def per ridefinire le variabili, quanto è considerato immutabile?

8

Cercando di imparare Clojure e non si può fare a meno di sentirsi dire continuamente come Clojure si basa su dati immutabili. Ma puoi ridefinire facilmente una variabile usando def giusto? Capisco che gli sviluppatori di Clojure lo evitino, ma potresti evitare di cambiare le variabili in qualsiasi lingua allo stesso modo. Qualcuno può spiegarmi come questo sia diverso, perché penso che mi manchi questo dai tutorial e dai libri che sto leggendo.

Per dare un esempio come va

a = 1
a = 2

in Ruby (o blub, se preferisci) diverso da

(def a 1)
(def a 2)

in Clojure?

    
posta Evan Zamir 08.01.2016 - 18:21
fonte

3 risposte

8

Come già notato, il fatto che la mutabilità sia scoraggiata in Clojure non significa che sia vietato e che non ci siano costrutti che lo supportano. Quindi hai ragione che usando def puoi modificare / mutare un legame nell'ambiente in modo simile a ciò che l'assegnazione fa in altri linguaggi (vedi la documentazione di Clojure su vars ). Modificando i collegamenti nell'ambiente globale si cambiano anche gli oggetti dati che utilizzano questi collegamenti. Ad esempio:

user=> (def x 1)
#'user/x
user=> (defn f [y] (+ x y))
#'user/f
user=> (f 1)
2
user=> (def x 100)
#'user/x
user=> (f 1)
101

Notare che dopo aver ridefinito il binding per x , anche la funzione f è cambiata, poiché il suo corpo usa quel legame.

Confrontalo con linguaggi in cui ridefinire una variabile non cancella il vecchio binding ma solo shadows , cioè lo rende invisibile nell'ambito che viene dopo la nuova definizione. Guarda cosa succede se scrivi lo stesso codice in SML REPL:

- val x = 1;
val x = 1 : int
- fun f y = x + y;
val f = fn : int -> int
- f 1;
val it = 2 : int
- val x = 100;
val x = 100 : int
- f 1;
val it = 2 : int

Notare che dopo la seconda definizione di x , la funzione f usa ancora il binding x = 1 che era in ambito quando è stato definito, cioè il legame val x = 100 non sovrascrive il precedente legame val x = 1 .

Bottomline: Clojure consente di mutare l'ambiente globale e ridefinire i binding in esso. Sarebbe possibile evitare questo, come fanno altri linguaggi come SML, ma il costrutto def in Clojure ha lo scopo di accedere e mutare un ambiente globale. In pratica, questo è molto simile a ciò che l'assegnazione può fare in linguaggi imperativi come Java, C ++, Python.

Ancora, Clojure fornisce molti costrutti e librerie che evitano le mutazioni, e puoi fare un lungo cammino senza usarlo affatto. Evitare la mutazione è di gran lunga lo stile di programmazione preferito in Clojure.

    
risposta data 08.01.2016 - 19:41
fonte
2

Clojure is all about immutable data

Clojure significa gestire lo stato mutabile controllando i punti di mutazione (cioè, Ref s, Atom s, Agent s e Var s). Anche se, ovviamente, qualsiasi codice Java che usi tramite Interop può fare quello che vuole.

But you can easily redefine a variable by using def right?

Se intendi associare un Var (al contrario di, ad esempio, una variabile locale) a un valore diverso, allora sì. Infatti, come indicato in Vars e l'ambiente globale , Var s sono specificatamente inclusi come uno dei quattro "tipi di riferimento" di Clojure (anche se direi che si riferiscono principalmente a dynamic Var s lì).

Con Lisps, esiste una lunga storia di attività interattive di programmazione esplorativa tramite REPL. Ciò comporta spesso la definizione di nuove variabili e funzioni, nonché la ridefinizione di quelle vecchie. Tuttavia, al di fuori del REPL, re def in Var è considerato di scarsa qualità.

    
risposta data 26.05.2016 - 05:02
fonte
1

Da Clojure for the Brave and True

For example, in Ruby you might perform multiple assignments to a variable to build up its value:

severity = :mild
  error_message = "OH GOD! IT'S A DISASTER! WE'RE "
  if severity == :mild
    error_message = error_message + "MILDLY INCONVENIENCED!"
  else
    error_message = error_message + "DOOOOOOOMED!"
  end

You might be tempted to do something similar in Clojure:

(def severity :mild)
  (def error-message "OH GOD! IT'S A DISASTER! WE'RE ")
  (if (= severity :mild)
      (def error-message (str error-message "MILDLY INCONVENIENCED!"))
  (def error-message (str error-message "DOOOOOOOMED!")))

However, changing the value associated with a name like this can make it harder to understand your program’s behavior because it’s more difficult to know which value is associated with a name or why that value might have changed. Clojure has a set of tools for dealing with change, which you’ll learn about in Chapter 10. As you learn Clojure, you’ll find that you’ll rarely need to alter a name/value association. Here’s one way you could write the preceding code:

(defn error-message [severity]
   (str "OH GOD! IT'S A DISASTER! WE'RE "
   (if (= severity :mild)
     "MILDLY INCONVENIENCED!"
     "DOOOOOOOMED!")))

(error-message :mild)
  ; => "OH GOD! IT'S A DISASTER! WE'RE MILDLY INCONVENIENCED!"
    
risposta data 23.04.2016 - 00:09
fonte

Leggi altre domande sui tag