Hacker Newsnew | past | comments | ask | show | jobs | submitlogin

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.



How 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!


For 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.


I 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.


What 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.


Sorry 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.


Well 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).



I'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?


Through an 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.


That's a good explanation. Thanks!


You 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.


OK. 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.


Yeah, Clojure is one of those languages where functional purity at all costs isn't really the goal. Like others have said its more about being careful how you set state and Clojure gives you loads of great functions for doing everything in a pure way. Its standard library is its greatest asset as far as I'm concerned


Exactly! 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.).


Exactly. Functional programming is pretty much all about being very mindful of when to use state vs pure functions.


Using 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.


There's also the added benefit of Software Transactional Memory being available for operations on said mutable state, in case you need to access it from multiple threads.


Apologies 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) 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.




Guidelines | FAQ | Lists | API | Security | Legal | Apply to YC | Contact

Search: