简体   繁体   中英

clojure: what's the equivalent of scheme's `set!`

I'm trying to to the exercises of Structure and interpretation of computer programs in clojure. However, in multiple places it uses scheme's set! which changes the value of a variable inside a closure. eg

(define (stash x)
  ;; return a procedure which returns a value, which
  ;; you can change by providing an argument
  (lambda (nv-maybe)
    (if (null? nv-maybe)
        x
        (begin
          (set! x (car nv-maybe))
          x))))

I tried clojure's set! function, but it seems to work in a different way. What's the equivalent (or, closest alternative) of scheme's set! in clojure?

This exercise strikes me as a more than a bit contrived (OK, highly contrived). Here is how I would do it in Clojure (if I were forced to):

(ns tst.demo.core
  (:use demo.core tupelo.core tupelo.test) )

(defn new-stash
  "Saves an initial value and returns a function `stash` that can retrieve the 
   value via `(stash)`. Value can be mutated via `(stash <new-val>)` "
  [some-val]
  (let [local-state (atom some-val)]
    (fn
      ([] @local-state)
      ([new-val] (reset! local-state new-val)))))

(dotest
  (let [stash (new-stash 5)]
    ; stash saves the value 5
    (is= 5 (stash))
    (is= 5 (stash))

    ; mutate value in stash to 7
    (is= 7 (stash 7))
    (is= 7 (stash)) ; stash now contains 7
    (is= 7 (stash))))

Of course, the above is highly non-ideomatic Clojure code. Normally, you would just use an atom directly and avoid all of the closure junk:

(def stash (atom :uninitialized)) ; could use `nil`

(dotest
  (reset! stash 5) ; save the initial value
  ; stash saves the value 5
  (is= 5 @stash)
  (is= 5 @stash)

  ; mutate value in stash to 7
  (is= 7 (reset! stash 7))
  (is= 7 @stash) ; stash not contains 7
  (is= 7 @stash))

You could achieve the same result in other ways (by using a var , for example), but an atom is the simplest way of handling mutable state.

Note : This answer is obsolete because the OP has edited the question to include the "why" that I asked for. I'm leaving it in place for context, but Alan Thompson's answer is much better for the current version of the question.


It depends on what you are mutating and why. In some Scheme styles, mutation is commonplace, and so you might ask "how do I mutate a thing in Clojure" because your Scheme algorithm involves mutation. A better question to ask is, "what is the right algorithm to solve this problem in Clojure". Asking very broad questions about very narrow code snippets will not be productive.

For example, your code snippet is actually not useful at all, because even in Scheme it is analogous to:

(define (f x)
  (lambda (newvalue) '()))

That is, the mutation of x is immaterial in your function, because it is impossible to ever read x ! So, telling you the Clojure equivalent of this is not very interesting: it is simply

(defn f [x]
  (fn [newvalue] nil))

but of course this doesn't answer your real question. So the point is to back up: what is your real question? Why are you mutating this thing? Then responders can suggest the kind of mutation that bets fits your scenario, or (probably) an alternate approach that needs no mutation after all.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM