Fork me on GitHub
#beginners
<
2019-02-01
>
Mitch01:02:10

Does anyone know of any places in the DC area that use clojure? I'm enjoying learning the language but am not sure if there are really career prospects for someone who has never had a dev job before.

donaldball03:02:13

#jobs or #remote-jobs may have leads but my previous and current employers were and are based in DC and use clojure. I am dimly aware of a couple of consultancies in the area that do a good bit of clojure as well. It’s probably not as easy to find an entry-level clojure job as other langs, but it is possible.

john03:02:51

Yeah, we're a full clojure shop in Baltimore and I know some other shops that dabble in it. They're around.

Mitch03:02:19

Just nice to know it exists. Thanks! I'm going to keep plodding away and set the goal to get there someday.

Sturm02:02:44

😞 just wasted half an hour to figure out that pre-conditions have to come before the docstring

Sturm03:02:06

or maybe it's more accurate to say immediately after the argument list

hiredman03:02:29

Also your docstrings are in the wrong place

hiredman03:02:46

They go before the argument list

Sturm03:02:32

oh dear, thanks @hiredman, I hadn't realised this!

hiredman03:02:51

A linter like eastwood can be helpful catching that kind of thing (it is a super common thing to do)

andy.fingerhut23:02:04

There have even been a function or two inside of Clojure's implementation itself that had the doc string in the wrong place (now fixed, I believe)

Daouda05:02:11

hey folks, i trying to understand the code below which is a part of clojure fn implementation. But till now no success. can anyone help please.

body (if post
          `((let [~'% ~(if (< 1 (count body)) ===> ~'% resolve to %. so % is a local var name? ~(if (< 1 (count body)) = (if (< 1 (~count ~body)) ?
                      `(do [email protected]) ===> (do (list body) ?
                       (first body))]
               [email protected](map (fn* [c] `(assert ~c)) post)  ===> (list map (fn* [c] (assert ~c)) post) ? what is being done here?
               ~'%))  ===> % ?
            body)

hiredman05:02:54

Fns have a more complicated syntax then is usually used, and that is a complicated hand rolled parser for it

hiredman05:02:50

post conditions have access to the result of the function bound to '%' which is what is being setup there, ~'% doesn't resolve to %, but it splices the symbol % into the syntax quote surrounding it (if you don't splice a symbol in like that syntax quote will namespace qualify it, and namespace qualified symbols cannot be used for locals)

Daouda06:02:51

My bad, i meant exactly what you describe but used the wrong verb

hiredman05:02:57

@ is unquote splice, `(1 @[2 3] 4) => '(1 2 3 4)

hiredman06:02:20

you seem to have a hard time keeping quotations straight, so I would suggest investing time in that

hiredman06:02:58

that if is not quoted, so unquoting count and body would be really weird

hiredman06:02:19

(and not work)

Daouda06:02:11

@hiredman

[email protected](map (fn* [c] `(assert ~c)) post)
can you tell me what this line is for? cause actually what is being return is ~'%

hiredman06:02:58

it builds the post conditions

hiredman06:02:32

the map results in a seq like ((assert ...) (assert ...)) where ... is filled in by one item from post

hiredman06:02:48

[email protected] splices the seq in to the syntax quote

hiredman06:02:01

back tick is syntax quote

Daouda06:02:21

Thanks for this link, i got to understand the difference between

` and ' 
😄

hiredman06:02:25

in macros, you are constructing a form in some meta language to be interpreted in a language, in this case both the meta language and the language are clojure

hiredman06:02:48

a quotation is a literal bit of the language embedded in the meta language

hiredman06:02:53

a syntax quote is a templated version of a quote, meaning a mostly literal bit of the language, but you can fill bits in from the meta language

ben10:02:44

Is it poor etiquette to bump this? I’m still fighting with this a bit and starting to feel like I’m going around in circles

danieroux11:02:00

I went with https://github.com/cognitect-labs/aws-api for my SQS work. It works very well.

Wes Hall13:02:27

Beyond even this, I would suggest that actually working with the basic Java AWS library is relatively straightforward with interop. I mostly just do that these days. The Java library is well maintained, complete and consistent. Wrapping Java interop calls in simple clojure functions works really nicely and also doesn't isolate you from the details too much.

ben13:02:18

Thanks both - this is really useful. When I looked at the Java library, it didn’t have anything for mocking (or something similar) out of the box. Did I miss something? I’m currently looking at https://github.com/localstack/localstack for manual testing

Sy Borg14:02:10

I'm learning by solving Project Euler problems. For the Problem 2 I've come up with smth like:

(defn sum-even-fibs-upto [n]
  (let [fib-seq (lazy-cat [0 1] (map + (rest fib-seq) fib-seq))]
        (apply + (filter even? (take-while #(< % n) fib-seq)))))
It probably is not the most efficient, but my question is - can somebody show me an example how it can be re-written with a transducer?

chrisulloa15:02:16

@sy_borg I would probably do it like this.

(defn fib 
  ([]  (fib 1 1))
  ([a b] (lazy-seq (cons a (fib b (+ a b))))))

(defn fib-even-sum-up-to [n]
  (transduce (comp (take-while #(< % n)) (filter even?)) + (fib)))

chrisulloa15:02:32

Transduce is essentially transform and reduce, your transformation is (->> fib-seq (take-while #(< % n)) (filter even?)) and your reduce is +

Chase17:02:20

I'm trying to wrap my head around this example

(reduce (fn [new-map [key val]]
          (assoc new-map key (inc val)))
        {}
        {:max 30 :min 10})
; => {:max 31, :min 11}
I think it's the destructuring part in the anonymous function that gets me. So it seems we are feeding it one map but the variables are calling for new-map and then destructuring that same map into key and value?

chrisulloa17:02:35

when the map gets passed into the reduce function, entries are passed as MapEntry which looks like this [:key val]

dtsiedel17:02:44

When you reduce over a map, each entry is a MapEntry (small vector)

dtsiedel17:02:52

oops sorry @christian.gonzalez

chrisulloa17:02:10

haha no problem xP

Chase17:02:05

so it's seeing it as ([:max 30] [:min 10])

chrisulloa17:02:58

yup, and the reason you don’t destructure new-map is because the init value is {}

chrisulloa17:02:46

and every subsequent new-map is an entire map, where the [key val] is just a chunk of the {:max 30 :min 10} you are passing in

chrisulloa17:02:57

it probably helps to print out in the anonymous function

Chase17:02:24

how do i do that? I think I still need help seeing what's happening.

Chase17:02:44

i really wish I could just see what the evaluation steps are in functions step by step

noisesmith17:02:42

you can just add a println inside the fn

noisesmith17:02:08

(before the assoc, so you don't change the return value)

chrisulloa17:02:34

(reduce (fn [new-map map-entry]
          (let [[key val] map-entry]
            (println new-map)
            (println map-entry)
            (println key val)
            (assoc new-map key (inc val))))
        {}
        {:max 30 :min 10})

chrisulloa17:02:51

you could also destructure in a let binding to see the map entry before it gets changed

Chase17:02:47

ahhh, cool. This will help. Let me wrap my brain around this. ty

zlrth17:02:06

i have a bunch of misunderstandings about exceptions and logging: i set the default uncaught exception handler as per https://stuartsierra.com/2015/05/27/clojure-uncaught-exceptionsf

user> (Thread/setDefaultUncaughtExceptionHandler
       (reify Thread$UncaughtExceptionHandler
         (uncaughtException [_ thread ex]
           (println "dummy text"))))
nil
user> (/ 1 0)
java.lang.ArithmeticException: Divide by zero ;; plus i get the whole stacktrace
;; i incorrectly expected it to print "dummy text"
- i would think "uncaught exceptions" are any exceptions not (try ... (catch)'d . but it looks like the above is actually Thread/setDefaultUncaughtExceptionHandlerThatsOffTheMainThread. - i'm trying to understand this because i actually want extra information whenever i get an exception in production. i'd like to log the bindings in the lexical environment. i found taoensso.timbre/get-env, which does what i want, but i can't get main-thread exceptions to show up there without an explicit (log/error stuff). - i want what i consider uncaught exceptions to go to timbre, but i don't know what i don't know

noisesmith17:02:20

often the most succinct thing is to pprint a hash-map (clojure.pprint/pprint {:new-map new-map :map-entry map-entry :key key :val val})

noisesmith17:02:38

@mfm by the time the exception is thrown, the lexical environment is ready to be garbage collected - in fact much of it can be collected before it even exits (and clojure does so, it's called locals clearing)- you can throw ex-info in a catch clause in order to provide a hash-map with extra data, and test for that in your handler, but it's a fundamental problem

zlrth17:02:52

very interesting. thank you. dumb question: how do you know this? that is, where can i look up more info?

noisesmith17:02:08

I've frequently ended up in slightly awkward nesting of code (or using a delay defined in one let block, then forced inside a try, so that the value if any can be used in a catch) - these things are awkward, they conflict with the semantic model of bindings that clojure is trying to create

noisesmith17:02:41

I've picked it up by following discussions around bugs and debugging tools, I don't know a good reference outside the clojure code itself

noisesmith17:02:55

clojure's jira and changelog would also likely be enlightening

zlrth17:02:14

ok thanks. googling locals clearing has yielded some good stuff

noisesmith17:02:44

yeah - often when running a proper debugger you need to increase resource usage by turning off locals clearing

noisesmith17:02:08

and it can fundamentally break some code (eg. using a very long lazy-seq and now you are holding the head...)

zlrth17:02:22

is it reasonable for a webapp to disable locals clearing in production?

zlrth17:02:28

ah by your second comment, perhaps not

noisesmith17:02:35

depends, how much ram can you afford

zlrth17:02:05

damn. our production box is 32gb. are we talking gigabytes or terabytes?

noisesmith17:02:44

this is highly variable, it really depends on how much of your code is implicitly relying on locals clearing - you could try locally and see what happens to resource usage?

zlrth17:02:09

sounds good and interesting to me

noisesmith17:02:30

but it might be more straightforward to rearrange things so that local events and values that matter enough are logged or made available in an ex-info object

zlrth17:02:44

small question: what is the main thread exception handler? and how to i modify it? for use in timbre

noisesmith17:02:14

there's a defaultExceptionHandler on java.lang.Thread - that's used if the current thread doesn't have a default set - I might be misremembering that name, it's long and awkward

noisesmith17:02:45

there's also a property on each thread that holds a handler

zlrth17:02:09

ok cool i'll look at that. the answer might be: "by default timbre does capture the main-thread-exceptions" and i have misconfigured something

noisesmith17:02:10

you can access the current thread in order to set it's handler with (Thread/currentThread)

zlrth17:02:18

oO thanks

noisesmith17:02:27

I don''t think this has anything to do with Timbre, I'd be mad if Timbre was going and changing my uncaught exception handlers without me explicitly making it do so

noisesmith17:02:00

for example in my app we want to hit a rest api to make a pagerduty alert -that's not Timbre's job, we also log in the same handler, but that's separate

noisesmith17:02:39

in an app that's critical enough, you might even ask the whole vm to shut down if certain unexpected conditions are hit (that's safer than running in unknown bad state)

lilactown17:02:13

I’ve wonder if that’s something that a component/system framework could help with (similar in vein to integrant/etc.)

lilactown17:02:08

like if you have this thing starting and defining your internal deps/resources, I wonder if that’s the correct point in the system to take control of things like exceptions and recovery as well

noisesmith17:02:44

@lilactown the difference for my usage is that the default uncaught exception handler and recovery logic aren't stateful - they are an idempotent setting that I can safely do each time -main is invoked

noisesmith17:02:56

I guess they do rely on config (loading up proper logging settings and knowing which endpoint to hit with info about emergency states), but that's similarly not a stateful thing per se, they are loaded up on startup and idempotent

noisesmith17:02:33

(by idempotent I of course mean that loading my configs twice gives me the same result as loading once, so it's safe to just do every time -main is launched)

noisesmith17:02:34

as opposed to what integrant and friends are for, where you don't want N db connection pools sitting around, and you can't own the same port with two server instances

lilactown17:02:46

sure. I think that’s agreeable with what I’m saying

noisesmith17:02:05

in my first app using component, the configs were a component

lilactown17:02:08

I think that’s simply a function of some inflexibility of integrant and friends

noisesmith17:02:13

in retrospect that just increased complexity

noisesmith17:02:42

I think it's becuase integrant and friends are for a specific task, and that task isn't "startup", it's "managing stateful resources"

lilactown17:02:49

maybe the comparison is bad, yeah

lilactown17:02:09

I mean, I think they’re related

lilactown17:02:27

e.g. I could imagine a framework that would allow you to “load” a system, and subsequent “loads” would only restart resources that needed to change

lilactown17:02:14

e.g. I updated my route handler so I need to restart my webserver, but no need to restart my db pool.

lilactown17:02:09

maybe I just need to create an example framework 😄 I think the comparison with component/integrant is bad, you’re right

noisesmith17:02:01

I'm not saying it's a bad comparison, but my experiences structuring an app with component led me to differentiating things that are idempotent (safe to simply run on every restart) vs. things that use or define fragile state (that requires careful startup order, and specific shutdown steps)

lilactown17:02:07

FWIW I don’t appreciate component or integrant as much as others due to the way it warps your app like you’re saying 😛

Mario C.17:02:42

Good morning gentlemen! I have a query (str "SELECT " fields " FROM interviews WHERE _id IN (" interview-ids ")"). I have a list of interviews ids and I want to select certain fields from those interviews. Is this the best way I can do it?

Mario C.17:02:10

(sql/query url [query])

noisesmith17:02:10

I wouldn't use str to create a query

Mario C.17:02:50

Whats the issue with using str? 🤔

lilactown17:02:53

I think most libraries have some way of interpolating queries

noisesmith17:02:56

if you use the ? syntax in the string, and provide the substitutions via extra args in the query vector, you get a much safer parameterized query, which prevents injection

noisesmith17:02:44

if you just use str, anything in the fields or interview-ids strings could be an unintended sql command, or even an exploit if you use any user provided data

noisesmith17:02:15

"select a_field from foo where val > ?"

noisesmith17:02:32

then you provide an actual number, rather than putting a number into the string

lilactown17:02:52

@mario.cordova.862 what library are you using to do your sql query?

noisesmith17:02:27

that looks like just clojure.java.jdbc

Mario C.17:02:37

that is correct

Mario C.17:02:41

Perfect! But aside from the parameterized query is the query itself fine? Is there a better way to structure this query?

noisesmith17:02:51

if you do need to use string concatenation for some things (where the query can't be parameterized in the way you need), at least separate the parameters from the query construction, and if possible have query construction be static and verifiable

noisesmith17:02:22

that looks like reasonable sql, but I don't know your db and schema of course :D

Mario C.17:02:43

okay okay fair enough

noisesmith17:02:20

and these questions about query construction vs. parameterization is why people end up using higher level libs that abstract over clojure.java.jdbc (eg. hugsql, honeysql which is provided by the clojure.java.jdbc devs, etc.)

Mario C.18:02:59

After refactoring I have ["SELECT ? FROM interviews WHERE _id IN (?)" fields interview-ids] but this is not return anything. Using the same values for fields and interview-ids when I run (str "SELECT " fields " FROM interviews WHERE _id IN (" interview-ids ")") using (sql/query url [query]) it works.

Mario C.18:02:24

Any idea what I am doing wrong?

Mario C.18:02:13

interview-ids looks like this '123', '456'

seancorfield18:02:55

JDBC requires a separate ? placeholder for each value -- it doesn't automatically expand collections.

seancorfield18:02:50

So you'd need SELECT ?,?,?,... FROM interviews WHERE _id IN (?,?) for however many fields and interview-ids you have.

seancorfield18:02:12

(and I'm not sure that JDBC accepts ? for column names anyway?)

Mario C.18:02:20

What if I dont know how many interview ids I will have?

seancorfield18:02:43

(str/join "," (repeat (count interview-ids) "?")))

seancorfield18:02:45

So you end up with something like (into [(str "SELECT " fields " FROM interviews WHERE _id IN (" (str/join "," (repeat (count interview-ids))) ")"] interview-ids)

seancorfield18:02:41

(assuming interview-ids is a collection of IDs, not a comma-delimited string!)

Mario C.18:02:21

Doesn't this go back to using str to create a query?

seancorfield18:02:07

You're still creating a parameterized query.

seancorfield18:02:58

The above would produce (for interview-ids being [123 456]) ["SELECT ... IN (?,?)" 123 456]

seancorfield18:02:29

This is why a lot of people use things like HoneySQL since it handles this for you.

Mario C.18:02:38

Okay got it. And yea we probably should have used that. Maybe we can refactor and integrate into our project.

josh_tackett22:02:02

Anyone know a good clojure image->text library?

ghadi22:02:02

I bet @gigasquid knows