-
-
Save stephendeyoung/7720578 to your computer and use it in GitHub Desktop.
(defn actions [array-of-funcs] | |
(doseq [func array-of-funcs] (func))) |
(defn show-dropdown [e] | |
(this-as button | |
(let [$button (js/$ button)] | |
(.preventDefault e) | |
(remove-dropdown) | |
(.addClass (.parents $button ".dropdown") "open")))) |
(defn show-dropdown [e] | |
(this-as button | |
(let [$button (js/$ button)] | |
(actions | |
(array (fn [] (.preventDefault e)) | |
(fn [] (remove-dropdown)) | |
(fn [] (.addClass (.parents $button ".dropdown") "open"))))))) |
I don't see how this is any more pure than the original. There's a layer of indirection in there, but what does it buy you?
A functional approach to this problem may be: take a DOM value as input and generate a modified value as output. Unfortunately I don't know how to do DOM-as-a-value. I suppose you could string them together in the same way though:
(defn prevent-default! [[dom-node event]]
(.preventDefault event)
[dom-node event])
(defn remove-dropdown! [[dom-node event]]
;; whatever
[dom-node event])
(defn add-dropdown-class! [[dom-node event]
(.addClass (.parents dom-node ".dropdown") "open")
[dom-node event])
(defn show-dropdown! [e]
(this-as button
(-> [(js/$ button) e] prevent-default! remove-dropdown! add-dropdown-class!)))
I'm not sure I like this approach overall. It has the advantage of being explicit about when state is being mutated, and of using a somewhat more idiomatic way of stringing functions together. This is how I would do it if I did have DOM-as-a-value. But the individual handlers are somewhat bulky, and it's somewhat more code than the original version.
If I was reading someone else's code, I would be happiest to see your original 'impure' version, perhaps with a "!" at the end of the function name.
Good point @mullr, I think I understood the original posters code being that he wanted to do all the mutations in a separate function keeping the other functions as pure as possible but then again this notion of pure is probably not inline with the functional notion of pure. While I like the idea, your post has given me a time to reflect and I'm not sure how best to solve this, but I do like delegating the mutation to a single function thereby keeping the other functions pretty clean. I haven't implemented this in my own app just yet, perhaps I'll wait to see if a more clean approach surfaces during my own development.
Sorry for the delayed response. Thanks for your comments @frankhale and @mullr.
@mullr the reason I feel my pure implementation is pure is because there are no side effects in the show-dropdown
function. The side effects (preventing the default action, adding a class etc) are handled by the doseq
expression. I thought this might be a slightly cleaner way of doing things.
Unless I've misunderstood your implementation you have side effects in your prevent-default!
and add-dropdown-class!
functions because you have two statements: one to change state and then another to set the return value of the function.
However, as you've suggested I'm not sure what this approach really buys you. I was interested in how you could write more functional code in CLJS than JS but I've found that you still end up having a lot of side effects in CLJS (mainly because of the DOM API). I thought you could hide this ugliness in my actions
function but, to be honest, you could implement something like this in JS anyway.
I definitely like your 'pure' implementation. This looks much cleaner. I'm working on a Clojurescript app now and I am facing the same issues as you but I had not given it much thought. I think I'm going to borrow your ideas here and implement it in my app.