5 ms·
https://wiki.c2.com/?ClosuresAndObjectsAreEquivalent https://wiki.c2.com/?ClosuresAndObjectsAreEquivalent
by shock 6y ago
https://wiki.c2.com/?ClosuresAndObjectsAreEquivalent https://wiki.c2.com/?ClosuresAndObjectsAreEquivalent
- phoe-krk 6y agoI'm aware of this. How do you achieve setters if the state you close over is immutable? Isn't that the default in Clojure, since it seems to aim for immutability by default?
- harperlee 6y agoThrough an atom (https://clojuredocs.org/clojure.core/atom https://clojuredocs.org/clojure.core/atom). The idea of accesing a closure that is mutable is as follows: (let [temp (atom 0)] (defn getter [] @temp) (defn setter [val] (reset! temp val))) To implement an object, you would do something more like: (defn new-object [init-val] (let [temp (atom init-val)] {:getter (fn [] @temp) :setter (fn [val] (reset! temp val))})) (def obj (new-object 0)) ((:setter obj) 12) To define interfaces, you could check whether the map/object conforms to a spec, etc. But obviously all this is not very idiomatic; in clojure you would keep those functions first-class through defn instead of tying them to the object / map, and would pass state as an argument. Something like: (defn getter [obj] @obj) (defn setter [obj val] (reset! obj val)) (def obj (atom 0)) ; This gives you the ability (and need) ; to explicitly track the list of ; existing objects in use lest they are garbage collected. (setter obj 12) If obj has structure (e.g. it is a map such as {:type :my.personal/type :val 12}) you can identify its type through the type keyval and can check conformance to a spec, etc. As it was said in the previous post, it's equivalent. It's a matter of how to organize code.
- phoe-krk 6y agoThat's a good explanation. Thanks!
- richeyryan 6y agoYou could have an atom that holds the state and a function which can update that atom. You might do something like the elm architecture where you pass a message to a reducer which produces the new state that the atom is set to. All the logic is contained in the reducer and is pure and immutable and the state change happens at a single controlled point.
- phoe-krk 6y agoOK. If I understood correctly, then you limit the mutable state to one point, and use functional logic everywhere around it. You can also optionally close over the atom to achieve encapsulation. Thanks, TIL! That's equivalent to the "mostly functional" style that's doable in Common Lisp.
- harperlee 6y agoExactly! This method of limiting the mutable part to one point is a pattern that is highly useful, and highly used, when working in clojurescript with react through reagent and re-frame, for example: you represent the whole state of your app in a tree of maps and define functions that, given a version of your app state, return the updated version. Then you just execute state-changing pure functions linked to a stream of events to move your one-point-mutable-app-state through a stream of valid states. (and also obviously functions that map that state to HTML/CSS/SVG/etc.).
- marcofiset 6y agoExactly. Functional programming is pretty much all about being very mindful of when to use state vs pure functions.
- kazinator 6y agoUsing state is not functional programming, so you're really saying that functional programming is all about being mindful when to use functional programming versus when to abandon functional programming.
- 6y ago
- chris_j 6y agoApologies in advance for going on a slight tangent here but it's my view that defining getters and setters is not considered idiomatic OO code either. If one object is using getters and setter to access the state of another then this suggests that there feature envy and excessive coupling between those two objects. These days, I try to follow the Law Of Demeter (https://en.wikipedia.org/wiki/Law_of_Demeter https://en.wikipedia.org/wiki/Law_of_Demeter) and avoid that sort of thing. The logic that calls the getters and settings should really be in a class with a single responsibility that we call by invoking its methods in a tell-don't-ask style. As others have already pointed out, we can do similar things in a functional language like Clojure by encapsulating state in an atom (or an agent) and applying pure functions to that state.