4 ms·
One argument I can think of is that if your problem is not shaped in a functional way, then a functional programming language might be a poor fit for the job.
by phoe-krk 6y ago
One argument I can think of is that if your problem is not shaped in a functional way, then a functional programming language might be a poor fit for the job.
Common Lisp is multi-paradigm language that is pretty bendy with regard to absorbing new ways of doing programming. It gets actual work done as well.
- firepoet 6y agoHow is it that you think Clojure has trouble doing things in a non-functional way? I'm just curious, as I work in Clojure primarily and may have a huge blind spot I'd love to clear out!
- phoe-krk 6y agoI don't have much of experience with Clojure myself - the above might be my first impressions from the little bit where I've tried it. Well, let me try to fix them then, and ask some questions. In Clojure, how do you usually approach problems that are shaped in an object-oriented way? In object-oriented languages, this usually includes either some sort of method calls (and therefore mutable state) or message passing (which usually involves actors, like Erlang processes). Passing all state around as function arguments is one way, but it becomes somewhat tedious as state grows large.
- shock 6y agohttps://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.
- marcofiset 6y agoWhat would be an example of "problems that are shaped in an object-oriented way"? That just doesn't sound like a thing to me. That may originate from a bias you may have from developing primarily in OO languages. Clojure does have constructs to manage state. You don't need to shove it all into your function arguments.
- phoe-krk 6y agoSorry for the mind shortcut. By that, I meant all sorts of problems where multiple state-bearing entities communicate via message passing - usually implemented via method calls in OO languages.
- chris_j 6y agoWell Clojure most definitely does support state-bearing entities that communicate using message passing (channels / agents / atoms...) and, while I confess I that I personally don't use them in _quite_ the same way that I use objects and method calls in an OO language, I find that programming in OO and functional languages is kind of converging towards the same point: functional programming in the small (immutable value objects that are worked on using pure functions) and OO in the large (state-bearing entities communicating using message passing).
- LeonidasXIV 6y agoFor starters you'd need to have imperative control structures like `while`, `continue`, `break` and a `return` that basically returns from anywhere. That can probably be built with macros. Then you would need to have ways to mutate variables, so you'd need to stop using Clojure's persistent collections and instead use the ones provided by the JVM. Overall, you can do it if you want to, it is just somewhat painful to do so and the language will fight you. This is like my who tried to write functional Python which is possible but then it is just a much worse Clojure.