5 ms·
...any time a Clojure programmer want's to change the state of a variable, they must do so using the same kind of transaction management as they would use for a
by herdrick 16y ago
...any time a Clojure programmer want's to change the state of a variable, they must do so using the same kind of transaction management as they would use for a database.
Not a good thing. The word 'must' is the glaring red flag.
- herdrick 16y agoIt's not totally true, either; there are other ways to do state in Clojure. But none of them simply let the programmer, you know, change the state of a variable.
- lukev 16y agoSure they do. Thread-local vars behave pretty much like regular variables. Atoms also get you uncoordinated state.
- herdrick 16y agoOK, thanks for the tip about atoms. I hadn't dug into the var/atom/ref/agent docs. I should have before commenting. What I was looking for can be done with atoms, and is made more like traditional lisp mutation by defining a "st!" (or maybe "zet!" is a better name). (defn st! [atm v] (swap! atm (fn [_] v))) Now making ordinary lisp clojures is reasonable: (def fns (let [c (atom 0)] {:add (fn [x] (st! c (+ @c x))) :mlt (fn [x] (st! c (* @c x)))})) > ((get fns :add) 3) 3 > ((get fns :add) 3) 6 > ((get fns :mlt) -5) -30 Though I sure could do without the calls to 'atom' and @. Referencing and dereferencing is for C programmers. Maybe using swap! directly is better after all. (def fns (let [c (atom 0)] {:add (fn [x] (swap! c #(+ % x))) :mlt (fn [x] (swap! c #(* % x)))}))
- prospero 16y agoThe function you're looking for is 'reset!'
- herdrick 16y agoAh, thank you. But what I really want it something that doesn't require dereferencing syntax: (zet! c (* c x))
- die_sekte 16y agoThis would work: http://gist.github.com/536715 http://gist.github.com/536715 I'm not sure wether this is good style. Probably not. Edit: Bah. Fixed an error.
- herdrick 16y ago'replace-in-form' is a tree-replace, which in Clojure is done with clojure.walk/postwalk-replace. The clojure.walk package is basically tree-map stuff. Sadly, as far as I know, you need to roll your own tree-reduce.
- die_sekte 16y agoThanks for that. I did look at clojure.walk, I just didn't look close enough.
- prospero 16y agoIf you evaluate your expression on two different threads, the value of c is non-deterministic. On the other hand, if you call (swap! c (partial * 2)) on two separate threads, c will always end up 4x the original value. These limitations exist for a reason.
- weavejester 16y agoIt's worth noting you could also write: (swap! c * 2)
- prospero 16y agoYeah, I just thought that looked close enough to infix notation to be confusing.
- ihodes 16y agoIt is a good thing. And it's as simple as e.g. (dosync (ref-set ref newval)). In fact, if you find yourself doing it a lot (don't, please!) then define yourself a little function (defn rset [r v] (dosync (ref-set r v))). Must isn't that bad of a word, and it's a Lisp: what kind of boilerplate do you expect to write? I prefer it going on none. It's definitely worth checking out, and if you need state, and you'll need it rarely if at all, you're better served using the built-in STM and the wonderful functions that allow you to manage it (like add-watch; what a lifesaver in a state-heavy app like a GUI) to handle the mess.
- herdrick 16y agoI don't understand you. If you don't like to write boilerplate, why do you appreciate Clojure making mutation verbose? There are times when the imperative style is more natural.
- herdrick 16y agoI don't understand you. By this I merely meant, "I don't understand what you are saying". Hope that was clear.
- benreesman 16y agounless things have changed dramatically since earlier this year you can have a mutable cell called an 'atom' if you want. it will almost inevitably be a reference to an immutable data structure but it behaves just like any other mutable cell, and does not have to be wrapped in a transaction. besides, i think even truly single-assignment languages seem to have a place, just ask those crazy erlang guys ;) the way i read clojure's philosophy around state is: "you can fuck up the concurrency if you want but we're going to make it really hard to do it by mistake".
- runevault 16y agoOne thing others haven't mentioned, you still have the option of creating a normal java object and manipulating it with the standard method accesses. Those aren't forced to be immutable by Clojure. Fortunately doing that makes it fairly obvious what you're doing since it won't look like seq accesses, creating a code smell so you can decide if you're doing it too much as you get better with the language.
- KirinDave 16y agoIt's not strictly correct. You define variables all the time via vars. You must in the sense that if you want to do anything over time, since doing it other way is insane.