Fork me on GitHub
#clojure-sg
<
2017-05-25
>
renewdoit02:05:43

Generally good suggestion, although I can not agree with "avoid high-order functions"

spinningarrow02:05:34

Yes same here. I actually think his example using comp looks more readable than the one using anonymous functions

lucasbradstreet02:05:38

My thoughts exactly

tap03:05:56

Agree here. I use comp more and more often instead of nested anonymous function. Especially when I need argument function for filter, remove, sort-by, juxt, etc comp can be used to convert deeply nested function calls to a concatenation of self-contained functions. The other way to makes it easier to read is to use threading macro at the first position, but it still looks weird to me. For example, I would rather prefer comp here

(->> {1 {:id 1 :product {:name "Macbook Retina"}}} 
     (map (juxt key (comp #(str/replace % " " "-") str/lower-case :name :product val)))
     (into {}))
; => {1 "macbook-retina"}
more than
#(str/replace (str/lower-case (:name (:product (val %)))) " " "-")
or
#(-> % val :product :name str/lower-case (str/replace " " "-"))

tap03:05:50

threading macro approach would get a bit messier when we need to mix -> and ->>

jimmy04:05:46

tap: in the case you mix -> and ->> you can use as->

tap04:05:40

Yeah, sure but probably not in an anonymous function, right?

andrut03:05:51

I would probably create a function extracted-name or something like that which would use the threading macro and the map would just call the function 😉

spinningarrow03:05:17

Yesss. Self documenting code for the win 😉

tap03:05:38

That’s an option but I think it’s not always. I find myself less strict with descriptive name when I have pure functions and repl. I try not to overly create a new functions these days. It’s easy to run/play with a piece of code than dealing with naming confusion and conflicts to me 😉

spinningarrow03:05:43

Do you usually write code in the repl and then move it over to your source files, or type it first in the source files and then evaluate in the repl?

tap03:05:00

The later

tap03:05:20

If you wrap #(-> % val :product :name str/lower-case (str/replace " " "-")) in a function, you won’t reuse it very often anyway because it tights to structure of the input map. I might group str/lower-case and (str/replace " " "-") together but not with the others

tap03:05:22

I believe you probably don’t want something like (defn access-name-in-map [m] (-> m val :product :name)) as well

tap04:05:05

I believe it’s same problem Rich said about Class and how you have to create a lot of duplicated methods to work with specificity of types

andrut04:05:31

for me the tradeoff is still worth it when I look at:

(->> {1 {:id 1 :product {:name "Macbook Retina"}}} 
     (map (juxt key (comp #(str/replace % " " "-") str/lower-case :name :product val)))
     (into {}))
I have no idea what it’s doing… this I can read and understand:
(->> {1 {:id 1 :product {:name "Macbook Retina"}}} 
     (map product->name-in-param-case)
     (into {}))

lucasbradstreet04:05:03

Yeah, I agree that that one is begging to be pulled out

lucasbradstreet04:05:23

or at least use an anon fn in the form (fn [[k v]] ...)

lucasbradstreet04:05:51

I love map juxt combined with into {}, but sometimes you can push it too far

lucasbradstreet04:05:32

then again, I do kinda hate the #() anon fn form, so it could just be me

tap04:05:52

This is my personally opinion. For product->name-in-param-case, I have to jump to the source code to find out what it does. It might even have a surprise like side-effects inside. In many cases you need it, but not always. But for (comp to-slug :name :product val), I know it at a glance and fully understand what it does. And I believe it’s familiarizable. I think benefit of ability to see and understand more things in a single screen is not rated enough

tap04:05:52

However, I completely understand you all points. I definitely can see myself had the same opinion as you in the past. Just to give a different perspective.

lucasbradstreet04:05:29

(comp to-slug :name :product val) is fine with me

lucasbradstreet04:05:42

it’s once you start having to stick extra anonymous functions into the comp that I start thinking twice

tap04:05:21

yeah, that’s fair

lucasbradstreet04:05:26

occasionally I’ll use (comp (partial xx yy) …), I find that usually reads cleaner than additional anonymous functions

tap05:05:47

In that blog post, I also don’t fully agree with “Don’t rely on implicit nil-to-false coercion”. I think function like keep which operates on nil-to-false coercion is quite handy https://clojuredocs.org/clojure.core/keep

andrut05:05:02

@tap: after reading Clean Code, I actually agreed with the author that I don’t have to understand all the details when I’m reading through the code only if I’m interested in the details of implementation, I would go and check that after applying this rule more and more, our codebase became much nicer to read for others it makes sense for you now to have all the details in one place, but it will be hard for others and your future yourself 🙂 that’s my opinion 🙂

andrut05:05:06

keep is nice, never used it

tap05:05:03

@andrut I really really like Clean Code. It’s my first favorite programming book. And I do follow what the author said for years, religiously in some period of my career. It all comes down to finding a balance between verbosity and clarity. And I think Clojure gives us a possibility for different balance from static-typed OO language. I’m not advocate for doing code golf here, but if we can get a short code with clarity, why not? (comp to-slug :name :product val) have almost the same number of character as product->name-in-param-case I don’t expect a person who don’t familiar with comp to understand this, but I believe anyone who familiar with it will understand this statement at a glance like me. I believe we should try to utilize more what clojure provides. If anyone don’t understand the statement, go play with it in your repl. I believe it wouldn’t take more than a few times, you’ll feel that it’s natural. I do have somewhat similar problem with assoc when I started using clojure. I came from a place where we use put or add to put thing into a collection. And maybe because I’m not coming from native-English speaking country, the word ‘association’ didn’t ring that meaning to me. It’s not a word that I use often everyday. But I stick with it and one day it became natural. If I were to write code optimizing for myself who not familiar with assoc to read, I’ll have to alias it to something like immutable-put

(def immutable-put assoc)
I believe everyone here would agree that I should not do that.

andrut05:05:17

I agree (comp to-slug :name :product val) looks legit 🙂

andrut05:05:30

(juxt key (comp #(str/replace % " " "-") str/lower-case :name :product val)) doesn’t

tap05:05:15

what about (juxt key (comp to-slug :name :product val))?

andrut06:05:11

arguable, too much of mental overhead for me, but maybe I’m stupid 🙂

tap06:05:16

Nah, don’t go that way. I believe it’s just a familiarity. Again, totally understand you. Just trying to give an argument supporting my trade-off view. If we all agree with everything, we won’t advance, right?

melindazheng06:05:22

@tap “If we all agree with everything, we won’t advance” 👍