Fork me on GitHub
#clojure
<
2018-07-24
>
emccue00:07:28

Is there a function local let that isnt scoped?

emccue00:07:58

Im writing some pretty imperative selenium code and the nested lets are getting annoying

noisesmith00:07:41

any let binding can refer to bindings before it, and it's normal to use _ as a binding name for things done for side effects

noisesmith00:07:08

@emccue so you rarely need nested lets

user=> (let [a 1 _ (println a) b 3 _ (println (- b a))] (+ a b))
1
2
4

emccue00:07:45

Okay that underscore thing made it far more readable

noisesmith00:07:36

_ is just a name, but it's agreed to mean "I need to bind this but don't use it"

noisesmith00:07:05

so you can also use it with function args if your function is called by something that passes args you never need etc.

emccue01:07:21

id used it other places in my code like a (with-open [_ (DriverManager/getConnection url)] (println (<< "Created ~{filename}")) to create a sqlite db

emccue01:07:40

but I hadn't thought to use it for writing a list of commands

seancorfield01:07:40

Why are you using low-level JDBC stuff like that? You were asking about clojure.java.jdbc the other day -- did the documentation I pointed you to not help?

seancorfield01:07:30

(not meaning to sound accusing, just surprised to see that low-level Java interop stuff)

dpsutton01:07:28

i'm surprised to see with-open used like this. won't it need a var to call close on at the end of its scope?

noisesmith01:07:51

it uses the value directly - otherwise shadowing would cause bugs

noisesmith01:07:13

_ doesn't mean "throw away this value", it's just an idiomatic name for a thing you don't use despite needing to bind it

dpsutton01:07:28

i guess i hadn't thought about it before. yeah i was thinking _ has a good chance of being shadowed repeatedly

noisesmith01:07:22

right, but the with-open macro is immune to shadowing if you look at the source or expand it

dpsutton01:07:52

i just did. good eye

adam01:07:47

I am trying HTTP Kit but my changes are not showing until I restart the server. How do I develop this way 😮

noisesmith01:07:25

a common trick is to either use a lib like stuartsierra/component that lets you reload your http server in a repl with short turnaround time or defining #'app as your handler that you pass to the http server, so that when you redefine app in the repl it instantly uses the new value

noisesmith01:07:27

the issue is that if you call (long-running-thing foo) and long-running-thing repeatedly uses foo, how can it see new values? it turns out that #'foo is a var reference, so if foo was defined with defn, it will be a container that is updated when you redefine foo

noisesmith01:07:21

but regardless of which solution you choose, definitely do something that leaves a repl open so you can redefine things without restarting clojure

adam01:07:39

Thanks @noisesmith. I will try that.

noisesmith01:07:24

often your template already passes the app (or maybe handler) with a var quote, so all you really need is to evaluate the new defn and it just works

noisesmith01:07:46

@somedude314 there's a remarkable set of tricks we use to keep clojure running as we redefine things, the cool thing is most of them actually require us to write higher quality code to work properly :D

adam02:07:30

@noisesmith: I see. I am trying to apply the above 🙂

Lachlan Robertson05:07:09

Anyone have any ideas why with-redefs doesn’t want to redef gensym inside a map-indexed?

;; Fails: gensym redef ignored
(let [db {:qualifications ["foo" "bar"]}]
  (with-redefs [gensym (constantly "id")]
    (map-indexed
      #(vector %1 {:id (gensym) :text %2})
      (:qualifications db))))

;; Fails as above (using fn instead of #() doesn't seem to change anything?)
(let [db {:qualifications ["foo" "bar"]}]
  (with-redefs [gensym (constantly "id")]
    (map-indexed
      (fn [idx v]
        (vector idx {:id (gensym) :text v}))
      (:qualifications db))))

;; Success: Only works in the inner-most scope?
(let [db {:qualifications ["foo" "bar"]}]
  (map-indexed
    #(with-redefs [gensym (constantly "id")]
       (vector %1 {:id (gensym) :text %2}))
    (:qualifications db)))
I’d like to redef gensym for testing but it’s being a pain

dpsutton05:07:44

this is crazy interesting

user> 
(with-redefs [gensym (constantly "redef")]
  (->> (map (fn [_] {:id (gensym)})
            (range 4))
       (filter (fn [{:keys [id]}] (= id "redef")))
       #_count))
()
user> 
(with-redefs [gensym (constantly "redef")]
  (->> (map (fn [_] {:id (gensym)})
            (range 4))
       (filter (fn [{:keys [id]}] (= id "redef")))
       count))
4

dpsutton05:07:02

if you count it they are there. if not they are not there

Lachlan Robertson05:07:03

@dpsutton You are a legend; that just made the penny drop. It’s because map/map-indexed returns a lazy-seq which means the redef is never being realised properly. If you realise it - like with vec it works:

(let [db {:qualifications ["foo" "bar"]}]
  (with-redefs [gensym (constantly "id")]
    ;; Realise the seq here
    (vec
      (map-indexed
        (fn [idx v]
          (vector idx {:id (gensym) :text v}))
        (:qualifications db)))))

dpsutton05:07:24

ah of course

Lachlan Robertson05:07:00

I’ve been mapping too long it seems xD Maybe I should start napping - or hammocking? - instead

delitescere07:07:54

Anyone got ideas about how to do the transformation in perform and all-of better?

(def healthchecks
  {:db db-healthcheck
   :zipkin zipkin-healthcheck})

(defn- healthcheck-result [check]
  (try
    (let [res (check)]
      (if res
        [:success res]
        [:failure res]))
    (catch Exception e
      [:failure (.getMessage e)])))

(defn- all-of [flag result]
  {flag (vec (sort (map (fn [[n]] n) (filter (fn [[_ [x]]] (= flag x)) result))))})

(defn- perform [healthcheck-map]
  ;; {:name1 function2 :name2 function2 :name3 function3} ->
  ;; [[name1 [:success result1]] [name2 [:failure result2]] [name3 [:success result3]]] ->
  ;; {:success [name1 name3] :failure [name3]}
  (let [checks (map (fn [[name function]] [name (healthcheck-result function)]) healthcheck-map)]
    (conj (all-of :success checks) (all-of :failure checks))))

;; (perform healthchecks)

delitescere07:07:36

It "works" but it feels super clunky

delitescere07:07:56

I feel like there's a transpose of some sort that could be done

mg11:07:25

perform throws away the results?

exupero12:07:17

(defn perform [healthcheck-map]
  (->> healthcheck-map
    (map (fn [[name function]]
           [name (healthcheck-result function)]))
    (group-by second)
    (map (fn [[k vs]]
           [k (mapv first vs)]))
    (into {})))

delitescere23:07:43

Awesome, getting there thanks.

emccue18:07:23

what is the naming scheme y'all use for global constants

emccue18:07:44

I changed from ALL_CAPS to +this-thing+

emccue18:07:54

but I am not sure what the standard is

dominicm05:07:56

https://dev.clojure.org/display/community/Library+Coding+Standards > Don't use a special notation for constants; everything is assumed a constant unless specified otherwise.

madstap18:07:46

(def the-answer 42) ? Stuff not being constant is the exception.

👍 8
hiredman18:07:47

everything def'ed is a global constant

👍 4
justinlee19:07:03

I do tend to use ALL-CAPS for arbitrary parameterized variables. It’s true that all def’s are global constants, but not all symbols are global defs. I find it helps readability to distinguish.

Schpaa19:07:02

Still protected in the namespace though

arrdem21:07:39

I’ve adopted the +this-is-a-constant+ notation of late and think I prefer it to my previous Java style screaming snake case def’d constants.

hiredman21:07:13

what is the difference between that and just using def?

noisesmith21:07:09

instead of (def IMMUTABLE_THING 42) how about (def ᵐᵘᵗᵃᵇˡᵉ⁻ᵗʰᶦⁿᵍ (atom 42))

noisesmith21:07:45

maybe this should have been a twitter :poop: -post instead of a comment here...

hiredman21:07:46

well this is a hair to split here

hiredman21:07:23

(def x (atom nil))

hiredman21:07:26

x is not mutable

hiredman21:07:40

x is bound to an atom which is a mutable reference

noisesmith21:07:20

OK but alter-var-root is a thing, as is def...

hiredman21:07:48

and those are still a thing when use a name with special symbols in it

hiredman21:07:38

I mean, what is the nuance of meaning meant to be conveyed there, I ask because def means constant to me, so I wonder what hair is intended to be split between using def and using def plus a special name

hiredman21:07:28

is mutating vars the trendy new thing to do all the time?

adam21:07:52

Any idea why the live reloading is working for me with Compojure but not with bidi: https://bpaste.net/show/b0389b041e6d

hiredman21:07:04

you are passing a var in the compojure case

noisesmith21:07:38

@somedude314 remember when we talked about using var quote / passing the var in?

adam21:07:30

@noisesmith: Aha, adding that made it work! Thanks.

noisesmith21:07:01

to be fair someone else caught it first this time, but yeah, np

adam21:07:34

Thank you too @hiredman 🙂

dominicm05:07:56

https://dev.clojure.org/display/community/Library+Coding+Standards > Don't use a special notation for constants; everything is assumed a constant unless specified otherwise.