Fork me on GitHub
#clojure
<
2018-08-14
>
lilactown00:08:04

I wish there was something like for but with indices

lilactown00:08:18

having to use map-indexed is so much less ergonomic for certain problems

emccue00:08:51

I can help you out with that

emccue00:08:14

one second while i steal other peoples code and hack it at the repl

emccue00:08:41

does that do what you want?

noisesmith00:08:00

there's always this idiom: (for [[i x] (map-indexed vector l)] ...)

lilactown00:08:38

:thinking_face: that doesn’t iterate twice?

emccue00:08:55

my official answer: don't think about it, its fine

noisesmith00:08:58

it's a lazy-seq, it's realized on demand

noisesmith00:08:46

so it only needs to iterate once (but it does build an intermediate collection, which means some garbage that wouldn't get created in the lazy-seq, reduce, or loop version of the code)

noisesmith00:08:55

but any of those would be lower level

emccue00:08:38

or even ignoring the lazy semantics

emccue00:08:57

iterating twice and doing one thing each time is the same as iterating one and doing 2 things

noisesmith00:08:19

in terms of actual machine behavior that is not actually true

noisesmith00:08:27

chasing pointers is more expensive than math

Alex Miller (Clojure team)01:08:17

psst… chasing pointers is math :)

😆 4
andy.fingerhut07:08:48

Dereferencing a pointer to an address that is not in the cache is way more expensive than adding two values that are in cache.

andy.fingerhut07:08:16

or even way more expensive than doing 10 simple arithmetic ops on values that are in cache

noisesmith13:08:20

Yeah, that's exactly what I was getting at

tbaldridge12:08:54

Even worse, chasing pointers isn't parallelizable

tbaldridge12:08:41

a->b[10]->c can't be reordered at all by the processor

tbaldridge12:08:02

while scalar math operations can be re-ordered and therefore executed in parallel (on a micro-op level) by the processor.

noisesmith00:08:42

but most of the time you shouldn't have to care

noisesmith00:08:46

also, directly relevant to this case, on the jvm short lived objects (eg. our two element vectors) that only exist in a limited scope are actually cheap, there's a special case for it in the gc

noisesmith00:08:06

they become more expensive when they stick around and get accessed again

noisesmith01:08:12

I've probably spent too much time learning trivia about the jvm and functional programming, but the main point here is that intuitions about performance and resources that come from place oriented programming get trickier in the language and environment we use

emccue02:08:49

how do I supress the output from a certain module at the repl

emccue02:08:07

im trying out monger and the database logs are sortof getting in the way

soulflyer04:08:41

@emccue With emacs/cider the monger noise gets shuffled up and it doesn't interrupt whatever I'm typing at the repl prompt. Would be nice to be able to turn it off and on though. Wouldn't want to suppress it altogether, it's sometimes useful feedback.

emccue04:08:47

it doesnt interrupt my typing, but it definitely interrupts my reading

emccue04:08:33

because anything evaluated will scroll away to heaven as a flurry of cluster notifications flow on by

soulflyer04:08:31

yeah, it's a pain. Have to catch it by scrolling up a bit so the stream of notifications doesn't move it up any further. I'm really hoping someone has a better solution to this

psdp08:08:23

Newbie here, sorry if this question is stupid - Why is *command-line-args* becoming nil when running my program from a compiled jar file? it shows actual input values though when being called from lein run ...

miikka09:08:18

Hmm. I'm not sure *command-line-args* is expected to work with AOT compilation.

miikka09:08:57

If you have a -main function, it's called with the command-line arguments, though.

keymone09:08:38

is there a way to assign a name to existing anonymous function?

keymone09:08:58

i have a feeling it’s somewhere in metadata but my googling skills fail me

keymone09:08:31

purpose is to have better error message when that function fails

miikka10:08:14

@keymone You can do (fn my-function [...] ...) and my_function will show up in the stacktrace. (Also useful for writing recursive functions, because you can refer to my-function in the function body.)

keymone10:08:27

yeah, that bit i know, but i need a way to assign name to existing anonymous function that i didn’t create

keymone12:08:06

just to clarify - there’s no way to do anything to existing fn object so that it shows up in (str x) or the error message generated involving it?

tatut12:08:13

could you not wrap it (fn SPOT-THIS [& args] (apply not-my-anon-function args)) or something like that?

tatut12:08:28

if you just want something that is easier to see in a stacktrace

keymone12:08:39

yeah, that’s my last-resort option

lilactown15:08:04

is it possible to treat namespaces as a value?

ghadi15:08:46

no -- not like first-class modules in ocaml

😢 4
lilactown15:08:11

how’d you know exactly what I wanted? 😭

ghadi15:08:36

I've been around a while 🙂

4
mario-star 8
bronsa15:08:39

that said, if you really want to, there is a decently complete api for manipulating/extending/creating namespaces..

ghadi15:08:03

you can use protocols and reify to achieve some similar functionality

8
bronsa15:08:09

it's just not very convenient to use if you're trying to emulate functors

lilactown15:08:26

mainly I appreciate the aesthetics of file-as-a-unit in OCaml; the idea that you can kind of “pass around files” and transform them is quite nice

bronsa15:08:09

yeah well, that just wouldn't play well with clojure's evaluation semantics where the unit is an expression, not a file or a namespace

👍 4
lilactown15:08:01

yeah. my original thinking was to have an API where you could give it a namespace that had a few expected vars for certain functionality, and the API would simply call them.

lilactown15:08:13

sounds like it’s not going to be very nice to do

bronsa15:08:05

namespaces are not modules, even conceptually, if you want an abstraction/encapsulation mechanism, like @ghadi says, protocols/reify/defrecord are what you want

bronsa15:08:10

@dpsutton eugh don't use that :P

😂 8
ghadi15:08:03

agree with @bronsa who agrees with me: you want a protocol and various impls of it.

joelkuiper15:08:48

Is there a specific channel for java/JVM issues?

joelkuiper15:08:04

we’re seeing unexpected behaviour in or Clojure app with > 1TB of off-heap memory usage; which is incredibly hard to diagnose. As far as we know there aren’t any native calls to malloc, so the suspects are bytebuffers (either from the XML parsing or JDBC) or something completely else. But all the usual tools for JVM inspection fail here …

ghadi15:08:11

@joelkuiper JVM observability is really quite good, especially in this area. Do you use a particular metrics library? I suggest micrometer if not -- it has built in exporting of off-heap / on-heap buffers. Same with prometheus and its associated client libraries. The source of that data is JMX

ghadi15:08:53

If your hypothesis is correct, you should see a large size of off-heap buffers

joelkuiper15:08:26

hmm I’ll take a look at micrometer, jvisualvm wasn’t of any use

joelkuiper15:08:05

it’s being created at 1.5GB/s and is outside the heap and perm gen stuff (also the GC does /nothing/ :p)

ghadi15:08:23

GC doesn't apply to off-heap

ghadi15:08:53

have any mmaped files?

joelkuiper15:08:02

not in this particular bit of code

joelkuiper15:08:31

it’s really just a JDBC stream (from Postgresql) to an XML lib to RabbitMQ (to produce messages for upstream processing)

ghadi15:08:44

Spidey sense says to make sure the JDBC stream is lazy -- I forget the details, but large resultsets could be buffered in mem if you don't ask for a lazy realization

ghadi15:08:11

set fetchSize

4
joelkuiper15:08:28

yeah, that was also I hunch I had … but I /think/ that was being set. Let me double check 😛

dpsutton15:08:49

would jdbc stream be on the heap?

borkdude16:08:49

we have set fetch-size, so that’s probably not it. it’s been the same value for years now.

joelkuiper16:08:56

(deftype SqlReduce [conn query params query-timeout fetch-size ptf]
  clojure.lang.IReduce
  (reduce [this f]
    (.reduce ^clojure.lang.IReduceInit this f (f)))
  clojure.lang.IReduceInit
  (reduce [this f init]
    (trace "DB: reducing sql query" query params)
    (jdbc/with-db-transaction [tx conn]
      (let [rfn (^:once fn* [coll]
                 (let [coll (if ptf (pmap ptf coll) coll)]
                   (reduce f init coll)))
            opts {:fetch-size fetch-size
                  :timeout query-timeout
                  :result-type :forward-only
                  :concurrency :read-only}
            prepared (jdbc/prepare-statement
                      (:connection tx) query opts)
            sql-params (cons prepared params)]
        (jdbc/query tx sql-params {:result-set-fn rfn})))))

joelkuiper16:08:15

it’s set to fetch-size=1000 so that’s potentially not it

hiredman16:08:08

sorry, it doesn't matter, don't let my wtf side track this

joelkuiper16:08:30

the ptf can be somewhat expensive, so we were hoping that some parallelism might speed it up (essentially doing some record pre-processing)

joelkuiper16:08:03

this bit of code hasn’t changed though, and it used to work 😛

dpsutton16:08:57

check the versions of jdbc and the db drivers? try downgrading them to see?

joelkuiper16:08:17

narrowed it down to the creation of pg-objects when doing a database insert

joelkuiper16:08:28

it seems these objects are never cleared and exist off-heap

reborg16:08:54

@joelkuiper what psql, just normal jdbc?

joelkuiper16:08:36

we’re now seeing what object creation is the problem, could be cheshire as well (for some jsonb field)

joelkuiper16:08:16

it’s caused by a function like this one, but maybe not exactly this one

(defn clj->strarray
  "Returns a SQL string array object from a given collection.
  Returns nil if v is nil."
  [v]
  (when v
    (let [vals (map unkeyword v)
          vals (map #(str \" (str/replace % "\"" "\\\"") \") vals)]
      (pg-object "text[]" (str "{" (str/join ", " vals) "}")))))

joelkuiper16:08:50

looks like it’s a memory leak in cheshire

joelkuiper16:08:52

(defn clj->json
  "Returns a SQL json object from a given value.
  Returns nil if v is nil."
  [v]
  (when v
(pg-object "json" (cheshire/generate-string v))))

joelkuiper16:08:18

(or the underlying jackson or something)

schmee16:08:32

hmm… Cheshire is very widely used, you’d think a memory leak in the generate-string method would have been reported by now :thinking_face:

schmee16:08:52

Jackson even more so

joelkuiper16:08:10

we’re going to try swapping it for something else. Could also be the pg-object “json” thing

joelkuiper16:08:18

but it’s definitely that function

joelkuiper16:08:47

and agree, it seems to be far fetched … but so is generating 1,5GB/s of off-heap allocations for no reason 😛

schmee16:08:57

where is the pg-object function from?

dpsutton16:08:10

any chance you are interning a lot of keywords with cheshire?

joelkuiper16:08:26

yes that is definitely the case

joelkuiper16:08:29

(defn pg-object
  "Returns a new PGobject with type and value set."
  [type value]
  (doto (PGobject.) (.setType type) (.setValue value)))

joelkuiper16:08:38

from (:import org.postgresql.util.PGobject)

joelkuiper18:08:02

okay not cheshire. we were leaking a gzip inflater instance somewhere. kudos to the the excellent detective work of @wagjo!

Josh Horwitz18:08:40

Anyone getting a 503 from clojars currently?

tcrawley18:08:43

Hi @joshua.d.horwitz! At what url? On a deploy or get?

Josh Horwitz18:08:12

Toby! Long time no talk, a get

tcrawley18:08:59

Trying to pull down an artifact, or on the http://clojars.org site? The repo is served by a CDN, so is different machinery

Josh Horwitz18:08:24

pull down artifact, checked site and got a 503 as well, might just be on my end

tcrawley18:08:41

does http://clojars.org/ 503, or just a particular URL on the site?

tcrawley18:08:32

ah, http://repo.clojars.org is the CDN. It's working for me, is the 503 consistent for you, or intermittent?

Josh Horwitz18:08:49

consistent, must be on my end, I’ll restart and see what happens

tcrawley18:08:06

My pleasure! Holler if you have other issues.

bajrachar18:08:13

when folding over a map - I keep getting this error ClassCastException clojure.lang.PersistentHashMap$3 cannot be cast to clojure.lang.IFn clojure.core.reducers/fjinvoke

bajrachar18:08:55

It seems to error out after the first call to the combinef

noisesmith18:08:55

what does your fold look like?

bajrachar18:08:23

(r/fold sosy-merge sosy-reducer paths-to)

bajrachar18:08:27

where paths-to is a map

bajrachar19:08:00

` sosy-merge (fn ([] {}) ([m1 m2] (merge-with set/union m1 m2)))`

bajrachar19:08:55

`sosy-reducer (fn ([] {}) ([xs k v] (let [depth (inc curr-depth) paths (if (empty? path-prefix) (map #(vector (get (:rel-types db) (key %))) v) (reduce (fn [pths prefix] (reduce #(conj %1 (apply conj prefix (vector (get (:rel-types db) (key %2))))) pths v)) '() path-prefix)) cncpt (nth (:concepts db) k) tui (get-in db (conj (into [] cncpt) :tui))] (if (some #(contains? #{"T184" "T033" "T037" "T046"} %) tui) (reduce #(assoc %1 %2 (conj (get %1 %2 #{}) cncpt)) xs paths) (if (= depth 2) xs (merge-with set/union xs (get-symptoms-and-path db cncpt depth paths)))))))`

noisesmith19:08:20

why does the reducer function take 3 args?

bajrachar19:08:55

apparently for map - it takes 3 args - basically it splits into key val

bajrachar19:08:24

It was not working before with just 2 args - and I saw an example in the doc pointing to this issue

noisesmith19:08:26

no, a reducing function will always get two args, but you can destructure a map entry into a [k v] pair

noisesmith19:08:14

oh, never mind, that's weird

bajrachar19:08:23

it seems to always error out after the first call to combinef

bajrachar19:08:59

although I checked that it is doing what it is supposed to and that the args passed in are correct

bajrachar19:08:50

I am doing one thing though - that I am calling fold recursively - so basically the get-symptoms-and-path call will fold and return a intermediate map

noisesmith19:08:17

so get-symptoms-and-path ends up calling the code shown?

bajrachar19:08:25

well - seems to be some issue with folding over a map - when I change it to vector - I don't get the error

bajrachar19:08:17

may be I will try with a simpler example and see if it reproduces

noisesmith20:08:45

maybe it would simplify things to call seq or vec on the arg and use the two-arg reduce

👍 4
eigenhombre20:08:12

Multiple people on my team are reporting Clojars issues as well. Status page says everything is up, but downloading jars now fails with 503s.

4
dottedmag20:08:45

Has anyone tried to use Clojure to control IoT devices? I'm mostly interested in Z-Wave.

dottedmag20:08:06

There is openHab in Java, but, curiously, it does not allow scripting in Java (hence, no Clojure either).

ghadi20:08:30

wdym no scripting?

dottedmag20:08:58

No custom logic. openHab has a scripting language "when this event happens, do this", but it's can't load arbitrary Java code to react to events.

ghadi20:08:55

openHab doesn't need to know, it'll be our secret

dottedmag20:08:36

And even if it could, I dislike it so much I'd better use something else. It can't even survive disconnection and reconnection of Z-Wave gateway (USB stick) and has to be restarted, while the way to add a new Z-Wave device to the network is to take the gateway, bring it to the device, push buttons on gateway and device and plug the gateway back.

dottedmag20:08:55

@ghadi Have you managed to control openHab from Clojure?

ghadi20:08:28

nope. was thinking you can install a global event handler that dispatched to a clojure function, which would allow you to hot-reload/dispatch

dottedmag20:08:38

I'm reading the docs again and I think I see a better way: there is a REST interface with an events subscription endpoint. I'll try to use it rather than trying to load something into openHab itself.

tcrawley20:08:23

@eigenhombre can you give example jars that are failing? Is it all jars for you?

eigenhombre20:08:51

@tcrawley sent it to you out-of-band.

dbernal20:08:59

@eigenhombre @tcrawley we were having issues pulling lein-git-deps but it looks like it's ok now

eigenhombre20:08:54

@dbernal One team member who was reporting issues says it’s working for him now as well, thanks.

👍 4